Macromedia Coldfusion MX 7 Web Application Construction Kit
Cookies are simple variables that can be stored on a client machine. Basically, the server asks the browser to remember a variable with such-and-such a name and such-and-such a value. The browser returns the variable to the server as it requests successive pages from that same server. In other words, after the server sets the value on the browser, the browser continues to remind the server about it as the user moves from page to page. The net effect is that each site essentially has a small portion of the browser's memory in which to store little bits of information. NOTE Cookies first appeared in early versions of Netscape Navigator and have since been adopted by nearly all browser software. As of this writing, the original specification document for cookies is still available at http://www.netscape.com/newsref/std/cookie_spec.html. It is interesting to read, if only because it underscores how important Netscape's early innovations have become to today's Web. No substantive changes have been made to the cookies since. Introducing the COOKIE Scope
Cookies aren't something specific to ColdFusion. Any server-side scripting programming environment can set them, and they can even be set by client-side languages such as JavaScript. Depending on the language, the actual code necessary to set or retrieve a cookie varies a bit, of course. The best implementations keep coders from having to understand the details of the actual communication between the browser and server. It's best if the coder can just concentrate on the task at hand. In ColdFusion, the notion of cookies is exposed to you via the simple, elegant COOKIE scope. Similar to the APPLICATION scope you learned about in the previous chapter, the COOKIE scope is automatically maintained by ColdFusion. Setting a variable within the COOKIE scope instructs the browser to remember the cookie. Referring to a variable within the COOKIE scope returns the value of the cookie on the browser's machine. For instance, the following line asks the user's browser to remember a cookie variable called MyMessage. The value of the cookie is "Hello, World!": <cfset COOKIE.myMessage = "Hello, World!"> From that point on, you could output the value of #COOKIE.myMessage# in your CFML code, between <cfoutput> tags. The "Hello, World" message would be output in place of the variable. A Simple Cookie Exercise
This simple exercise will illustrate what happens when you use cookies. First, temporarily change your browser's preferences so that you will receive notice whenever a cookie is being set. To be notified when a cookie is set on your browser, follow these guidelines:
Now use your browser to visit the CookieSet.cfm template shown in Listing 20.1. You should see a prompt similar to the one shown in Figure 20.1. The prompt might look different depending on browser and version, but it generally will show you the name and value of the cookie being set. (Note that you can even refuse to allow the cookie to be set.) Go ahead and let the browser store the cookie by clicking OK. Figure 20.1. Click OK to let the browser store the cookie.
If you now visit the CookieShow.cfm template shown in Listing 20.2, you will see the message you started your visit at:, followed by the exact time you visited the code in Listing 20.1. Click your browser's Reload button a few times, so you can see that the value doesn't change. The value persists between page requests. If you go back to Listing 20.1, the cookie will be reset to a new value. Close your browser, reopen it, and visit the CookieShow.cfm template again. You will see an error message from ColdFusion, telling you that the COOKIE.TimeVisitStart variable doesn't exist. By default, cookies expire when the browser is closed. Therefore, the variable is no longer passed to the server with each page request and is unknown to ColdFusion. Listing 20.1. CookieSet.cfmSetting a Cookie
<!--- Filename: CookieSet.cfm Created by: Nate Weiss (NMW) Purpose: Sets a cookie to remember time of this page request ---> <html> <head><title>Cookie Demonstration</title></head> <body> <!--- Set a cookie to remember the time right now ---> <cfset COOKIE.TimeVisitStart = timeFormat(now(), "h:mm:ss tt")> The cookie has been set. </body> </html>
Listing 20.2. CookieShow.cfmDisplaying a Cookie's Value
<!--- Filename: CookieShow.cfm Created by: Nate Weiss (NMW) Please Note Displays the value of the TimeVisitStart cookie, which gets set by CookieSet.cfm ---> <html> <head><title>Cookie Demonstration</title></head> <body> <cfoutput> You started your visit at: #COOKIE.TimeVisitStart#<br> </cfoutput> </body> </html>
Using Cookies
You can easily build on the last example to make it a more useful in the real world. For instance, you wouldn't want the Time Started value to be reset every time the user visited the first page; you probably want the value to be recorded only the first time. So it would make sense to first test for the cookie's existence and only set the cookie if it doesn't already exist. It would also make sense to remember the full date/time value of the user's first visit, rather than just the time. So, instead of <cfset COOKIE.TimeVisitStart = timeFormat(now(), "h:mm:ss tt")>
you could use <cfif not isDefined("COOKIE.VisitStart")> <cfset COOKIE.VisitStart = now()> </cfif>
In fact, the isDefined test and the <cfset> tag can be replaced with a single <cfparam> tag: <cfparam name="COOKIE.VisitStart" type="date" default="#now()#"> This <cfparam> tag can be placed in your Application.cfc file so it is encountered before each page request is processed. You can now be assured that ColdFusion will set the cookie the first time the user hits your application, no matter what page they start on, and that you will never get a parameter doesn't exist error message, because the cookie is guaranteed to always be defined. As discussed previously, the cookie will be reset if the user closes and reopens her browser.
You could then output the time elapsed in your application by outputting the difference between the cookie's value and the current time. You could put this code wherever you wanted in your application, perhaps as part of some type of header or footer message. For instance, the following code would display the number of minutes that the user has been using the application: <cfoutput> Minutes Elapsed: #dateDiff("n", COOKIE.VisitStart, now())# </cfoutput>
The next two listings bring these lines together. Listing 20.3 is an Application.cfc file that includes the <cfparam> tag shown previously. Listing 20.4 is a file called ShowTimeElapsed.cfm, which can be used to display the elapsed time in any of the current application's pages by using <cfinclude>. You also can visit Listing 20.4 on its ownFigure 20.2 shows what the results would look like. Figure 20.2. Cookies can be used to track users, preferences, or, in this case, elapsed times.
Be sure to save Listing 20.3 as Application.cfc, not Application1.cfc. Listing 20.3. Application1.cfcDefining a Cookie Variable in Application.cfc
<!--- Filename: Application.cfc Created by: Raymond Camden (ray@camdenfamily.com) Handles application events. ---> <cfcomponent output="false"> <cffunction name="onRequestStart" output="false" returnType="void"> <cfparam name="COOKIE.VisitStart" type="date" default="#now()#"> </cffunction> </cfcomponent>
Listing 20.4. ShowTimeElapsed.cfmPerforming Calculations Based on Cookies
<!--- Filename: ShowTimeElapsed.cfm Created by: Nate Weiss (NMW) Please Note Can be <CFINCLUDED> in any page in your application ---> <!--- Find number of seconds passed since visit started ---> <!--- (difference between cookie value and current time) ---> <cfset secsSinceStart = dateDiff("s", COOKIE.VisitStart, now())> <!--- Break it down into numbers of minutes and seconds ---> <cfset minutesElapsed = int(secsSinceStart / 60)> <cfset secondsElapsed = secsSinceStart MOD 60> <!--- Display the minutes/seconds elapsed ---> <cfoutput> Minutes Elapsed: #minutesElapsed#:#numberFormat(secondsElapsed, "00")# </cfoutput> NOTE What is the meaning of output="false" and returnType="void" in the methods in listing 20.3? These are optional arguments that help define how CFC methods run. By using output=false, we limit the white space generated by the methods. Using returnType=void simply means that the method doesn't return any data. Again, these are optional attributes, but it's good practice to use them.
Because COOKIE.VisitStart is always a ColdFusion date/time value, getting the raw number of seconds since the visit started is easyyou use the dateDiff function. If the difference in seconds between the cookie value and the present moment (the value returned by the now function) is 206, you know that 206 seconds have passed since the cookie was set. Because most people are more comfortable seeing time expressed in minutes and seconds, Listing 20.4 does some simple math on the raw number of seconds elapsed. First, it calculates the number of whole minutes that have elapsed, by dividing SecsSinceStart by 60 and rounding down to the nearest integer. Next, it calculates the number of seconds to display after the number of minutes by finding the modulus (which is the remainder left when SecsSinceStart is divided by 60).
Gaining More Control with <cfcookie>
You already have learned how to set cookies using the <cfset> tag and the special COOKIE scope (Listings 20.120.3). Using that technique, setting cookies is as simple as setting normal variables. However, sometimes you will want more control over how cookies get set. Introducing the <cfcookie> Tag
To provide you with that additional control, ColdFusion provides the <cfcookie> tag, which is an alternative syntax for setting cookie variables. Once set, you can access or display the cookies as you have learned so far, by referring to them in the special COOKIE scope. Table 20.2 introduces the attributes available when using <cfcookie>.
Controlling Cookie Expiration
The most common reason for using <cfcookie> instead of a simple <cfset> is to control how long the cookie will exist before it expires. For instance, looking back at the Application.cfc file shown in Listing 20.3, what if you didn't want the Elapsed Time counter to start over each time the user closed her browser? Say you wanted the elapsed time to keep counting for up to a week. You would replace the <cfparam> line in Listing 20.3 with the following: <!--- If no "VisitStart" cookie exists, create it ---> <cfif not isDefined("COOKIE.VisitStart")> <cfcookie name="VisitStart" value="#now()#" expires="7"> </cfif> Controlling How Cookies Are Shared
Netscape's original cookie specification defines three additional concepts that haven't been discussed yet. All three have to do with giving you more granular control over which pages your cookies are visible to:
As a ColdFusion developer, you have access to these three concepts by way of the domain, path, and secure attributes of the <cfcookie> tag. As Table 20.2 showed, all three attributes are optional. Let's say you have three servers, named one.orangewhip.com, two.orangewhip.com, and three.orangewhip.com. To set a cookie that would be shared among the three servers, take the portion of the domain names they share, including the first dot. The following code would set a cookie visible to all three servers (and any other servers whose host names end in .orangewhip.com): <!--- Share cookie over our whole domain ---> <cfcookie name="VisitStart" value="#now()#" domain=".orangewhip.com">
The next example uses the path attribute to share the cookie among all pages that have a /ows at the beginning of the path portion of their URLs (the part after the host name). For instance, the following would set a cookie that would be visible to a page with a path of /ows/Home.cfm and /ows/store/checkout.cfm, but not /owintra/login.cfm: <!--- Only share cookie within ows folder ---> <cfcookie name="VisitStart" value="#now()#" path="/ows">
Finally, this last example uses the secure attribute to tell the browser to make the cookie visible only to pages that are at secure (https://) URLs. In addition, the cookie will expire in 30 days and will be shared among the servers in the orangewhip.com domain, but only within the /ows portion of each server: <!--- This cookie is shared but confidential ---> <cfcookie name="VisitStart" value="#Now()#" expires="30" domain=".orangewhip.com" path="/ows" secure="Yes">
NOTE You can specify that you want to share cookies only within a particular subdomain. For instance, domain=".intranet.orangewhip.com" shares the cookie within all servers that have .intranet.orangewhip.com at the end of their host names. However, there must always be a leading dot at the beginning of the domain attribute. You can't share cookies based on IP addresses. To share cookies between servers, the servers must have Internet domain names. The domain attribute is commonly misunderstood. Sometimes people assume that you can use it to specify other domains to share the cookies with. But domain can be used only to specify whether to share the cookies with other servers in the same domain.
Sharing Cookies with Other Applications
Because cookies aren't a ColdFusion-specific feature, cookies set with, say, Active Server Pages are visible in ColdFusion's COOKIE scope, and cookies set with <cfcookie> are visible to other applications, such as PHP, Perl, or JavaServer Pages. The browser doesn't know which language is powering which pages. All it cares about is whether the requirements for the domain, path, security, and expiration have been met. If so, it makes the cookie available to the server. TIP If you find that cookies set in another language aren't visible to ColdFusion, the problem might be the path part of the cookie. For instance, whereas ColdFusion sets the path to / by default so that the cookie is visible to all pages on the server, JavaScript sets the path to match that of the current page by default. Try setting the path part of the cookie to / so that it will behave more like one set with ColdFusion. The syntax to do this varies from language to language. Cookie Limitation
There are some pretty serious restrictions on what you can store in cookies, mostly established by the original specification:
|