Team LiB
Previous Section Next Section

HTTP Cookies

Although quite useful as a protocol, HTTP is known in the computer science world as a "stateless" protocol. If that doesn't make sense to you, don't worry. Unlike your favorite programs that you run on your desktop, such as a word processor, PHP has no way of remembering a past request. The only information PHP has available to it when a script is executed is the information it was provided during the HTTP request. For example, what if you wanted to make a website that remembers the first name of every visitor and uses it to generate personalized content from your PHP scripts? How would you distinguish one visitor from another one in your PHP scripts? Cookies are used to solve this problem in a (fairly) eloquent way.

If you're still not sure how cookies work, consider the concept of valet parking. When you pull up, the staff gives you a ticket and goes and parks your car. After you have eaten your meal or have seen your show, you return to the staff and present them with your ticket and they get your car. Now, if you didn't have that ticket, how would the staff know which car was yours? Furthermore, if you don't have a ticket, how does the staff know you ever parked a car in the lot in the first place? This concept of a valet parking ticket is exactly how cookies work. When you visit a website, the server gives you a cookie that identifies you. Next time you visit the website (or even go to another page on that website) you give that cookie back to the server and it uses it to determine who you are.

Cookie Features and Restrictions

When you're working with cookies, note that a number of restrictions have been put in place to both provide more functionality to the developer and prevent abuse. Because these cookies can be used to identify you, without such restrictions and guidelines they could be abused. Thankfully, there are a number of restrictions on when a browser will both accept or send a cookie to a Web server (although, as you'll see, they still aren't perfect).

In HTTP, a cookie is a segment of text no larger than 4 kilobytes (4096 bytes). Although any server can attempt to send a client browser a cookie, there is never a guarantee that the client browser will accept the cookie. Furthermore, a browser will send cookies back only to the same domains that created them. If my website coggeshall.org sets a cookie on a client browser, the browser will send that cookie back only to me and to no other server. Cookies can also be limited to apply to a specific part of the website (based on the path in the Web server). This means that I can set a cookie for coggeshall.org/mydirectory, which will apply only to that directory and its subdirectories.

A limitation also exists on how many cookies the browser will retain. Although this limitation can change from browser to browser, generally you should not expect more than 20 cookies per domain. If this limitation is reached, the standard policy is to delete the oldest of the cookies to make room for the new ones.

NOTE

In recent (6.0+) browsers, there seems to be no realistic limit to the number of cookies you can have.


Although a cookie can be no bigger than 4 kilobytes, there is no restriction on the actual data stored in the cookie (as long as it's text). Recall earlier in the chapter when I asked how you could write a script to remember a visitor's first name every time that person visits. For something as simple as this, you could send the user's browser a cookie that contained that person's first name. This cookie will then be sent back to your server every time that browser visits a page in the website and hence will be available from your PHP scripts.

NOTE

Although storing the user's first name in a cookie probably isn't an issue, it is simply to serve the purpose of an example of using cookies. It is never, ever, a good idea to store sensitive or personal information about your visitors in cookies. There are much more secure implementations of cookies I'll be describing later for such purposes.


One more commonly overlooked facet of cookies and the HTTP protocol is that cookies can be sent anytime a HTTP request is made. For instance, you don't have to send cookies when sending an HTML document. Any request for images, sounds, movies, and so on can be used as an opportunity to send a cookie. This is a common ploy of major Internet advertising firms. When you visit a website with an advertisement, often the banner itself attempts to send a cookie to the browser. Furthermore, this cookie isn't even for the website that you're currently visitingit is for the website that provided the image (the advertising firm). As is often the case, these advertising firms are used all over the Internet, so when you visit yet another website with these banners, your browser sends the cookie back to the firm, and it will be used to help target the advertisement being sent. Even with all the restrictions placed on cookies on the Internet, companies have still found ways to monitor your browsing habits.

Although not designed to be so much of a "security" feature to protect the visitor to the website, cookies also can expire after a certain amount of time. Cookies can be set to expire anytime after they have been set. After a cookie has expired, it is automatically destroyed by the client browser and will no longer be sent to the server. In cases where a cookie does not have a time limit associated with it, the browser will destroy the cookie when the browser is closed.

How Cookies Are Implemented

As I've stated earlier, a number of methods can be used to implement cookies in your PHP scripts. I'll discuss two methods (using pure HTML and using PHP) in this section. Setting cookies using client-side means, such as JavaScript, will not be discussed. Cookies themselves are sent to the client by specifying one or more Set-Cookie header(s) when a browser requests a file from the Web server during a GET or POST request. The syntax of a Set-Cookie HTTP header is as follows:

Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure

NOTE

Although every parameter can be specified, to function properly, the Set-Cookie HTTP header does not require every parameter. For example, the PATH parameter can be completely omitted.


NAME represents the name of the cookie, and VALUE is a URL-encoded value for the cookie. The DATE parameter represents the exact date and time when the cookie will expire (if desired), and PATH and DOMAIN_NAME represent the domain and path where this cookie applies. The final parameter, secure, should be specified if this cookie should be sent only over a secure HTTP (SSL) connection. As I mentioned, the DATE parameter represents the expiration date for the cookie. The format of this DATE is shown next:

<Day>, DD-MMM-YYYY HH:MM:SS GMT

<Day> is the full day (Monday, Tuesday, and so on), DD-MMM-YYYY is the date using the three-letter abbreviation for the month, and HH:MM:SS represents the time. It is important to note that all times should be in Greenwich Mean Time (GMT).

As I've mentioned, cookies are sent only to domains that match the domain of the cookie (specified by the DOMAIN_NAME parameter). It is important to note that the complete domain name is not necessary for the cookie to be set properly. For instance, if you wanted to set a single cookie that would work for mysubdomain.coggeshall.org, www.coggeshall.org, and simply coggeshall.org, a value for DOMAIN_NAME of .coggeshall.org would be sufficient. This works because the browser compares the current domain name against the cookie's value from right to left (backward) and sends the cookie if a match is made. Therefore, because mysubdomain.coggeshall.org does match .coggeshall.org (starting from the right and moving left), the cookie will be sent.

NOTE

If you do not specify a DOMAIN_NAME parameter for your cookie, the complete domain from which the cookie was sent will be used as a default value.


Note that I did not simply use coggeshall.org for the value of the DOMAIN_NAME parameter (there is a leading period). This is another safeguard against the misuse of cookies. The DOMAIN_NAME parameter must always have at least two periods in the domain name, and under certain circumstances, it must have three. This is to prevent cookies from being set with a domain name such as .com (which would match any domain ending in .com). To determine how many periods are needed for your DOMAIN_NAME parameter, you have to look at the top-level (.com, .edu, and so on) domain your cookie is being set from. There are a number of top-level domains in which only two periods are required for the domain name: .COM, .EDU, .NET, .ORG, .GOV, .MIL, .BIZ, .SHOP, .INFO, and .INT, to name a few. Also note that, as new top-level domains are created, they too will adhere to this standard. All other domains (such as .mi.us or .co.uk) require at least three periods in the DOMAIN_NAME parameter.

NOTE

Remember that earlier in the chapter I said that you shouldn't expect to store more than 20 cookies per domain? This applies only to cookies using the same DOMAIN_NAME value. For example, you can set 20 cookies for .coggeshall.org and then set 20 more for cool.coggeshall.org.


When working with cookies, the value of a cookie can be modified by sending an additional Set-Cookie header to its new value. Note that for this to work properly, you must set the cookie using the same domain, path, and name values used to set the original cookie. To delete a cookie, simply modify the current cookie's value so that it expires in the past.

To finish our discussion, let's show a complete example. Following is an example of a valid Set-Cookie header (see Listing 6.1):

Listing 6.1. A Valid Set-Cookie Header
Set-Cookie: mycookie=myvalue; expires=Tuesday, 03-Dec-2002 13:01:59 GMT;
            path=/; domain=.coggeshall.org;

NOTE

In the example found in Listing 6.1, the Set-Cookie header was broken into two lines because of limitations in the size of our page. In practice, the Set-Cookie header is sent as a single line of text.


As you can see, a cookie mycookie has been set with a value of myvalue for any files within the coggeshall.org domain. This cookie is also set to expire December 3, 2002, at 13:01:59 GMT.

Implementing Cookies in Your Scripts

Now that you are aware of how cookies are implemented, let's look at how this knowledge can be put to use to create cookies to be used from within your scripts. The most obvious method of setting cookies from PHP is to create a Set-Cookie header using PHP's header() function, shown in Listing 6.2:

Listing 6.2. Setting a Cookie Using the header() Function
<?php
     header("Set-Cookie: mycookie=myvalue; path=/; domain=.coggeshall.org");
?>

Because this function is used to send HTTP headers, it must be executed prior to any content being sent (such as that from an echo or print statement). Although functional, this is not the recommended way to set a cookie using PHP functions. In a moment I'll be discussing the setcookie() function, which is used for this task.

The second (and perhaps least obvious) method of setting cookies is through the use of HTML tags. Specifically, the <META> tag can be used to simulate HTTP headers from within HTML using the HTTP-EQUIV and CONTENT attributes. For example, to set the same cookie as in Listing 6.2, the following HTML can be used (see Listing 6.3):

Listing 6.3. Setting a Cookie Using the HTML <META> Tag
<HEAD>
<!-- other HTML here // -->
<META HTTP-EQUIV="Set-Cookie"
      CONTENT="mycookie=myvalue; path=/; domain=.coggeshall.org">
</HEAD>
<!-- the remainder of the HTML document here // -->

NOTE

The <META> tag is good for more than just setting cookies! Although it depends on the browser being used, most common browsers do have support for most HTTP headers when implemented in a <META> tag. For instance, you could redirect the browser to a new page using the HTTP Refresh header:


<META HTTP-EQUIV="Refresh" CONTENT="0; url=http://www.coggeshall
.org">


The third and probably most-used method for setting cookies is through the setcookie() PHP function. The syntax for setcookie() is as follows:

setcookie($name [,$value [, $expire [, $path [, $domain [, $secure]]]]]);

This function is used to both create and destroy cookies on the client browser. As is the case when sending any HTTP headers from your PHP scripts, this function must execute before any content is output to the client. Before we get into much detail on how this function works, let's briefly describe each parameter. For the most part, the purpose behind each of these parameters is the same as was described for the Set-Cookie header I discussed earlier. Specifically, $name represents the name of the cookie variable to set, $value represents the actual value of that cookie variable, $expire is a Unix timestamp representing the date and time to expire the cookie, $path is a string representing the path on the server that the cookie applies to, $domain is the domain for which this cookie applies, and finally, $secure is a Boolean indicating whether this cookie applies to secure HTTP only.

When using the setcookie() function, any of the optional parameters can be set to NULL if they are not needed. For instance, to set an identical cookie to that in Listing 6.2 when I used the header() function, I would use the setcookie() function as shown in Listing 6.4:

Listing 6.4. Using the setcookie() Function
<?php
    setcookie("mycookie", "myvalue", NULL, "/", ".coggeshall.org");
?>

If you'd like to modify a cookie's value, as is the case when working with the Set-Cookie header directly, you'll need to make sure the $path, $domain, and $name values of the setcookie() function are identical to those originally used. To delete a cookie using setcookie(), specifying the $value parameter of the function to NULL will do the trick (as long as $path, $domain, and $name are set properly, of course). An example of deleting the cookie set in Listing 6.4 is shown in Listing 6.5:

Listing 6.5. Deleting a Cookie Using setcookie()
<?php
    setcookie("mycookie", NULL, NULL, "/", ".coggeshall.org");
?>

After a cookie has been created, it will not take effect until the next time the browser requests another document from the Web server. To access a cookie value received from a browser, PHP provides the $_COOKIE superglobal array. This array is identical to ones you have already been exposed to, such as $_GET or $_POST, except it is used to store values of cookies. Each key in this superglobal array represents a single cookie (the key name is the cookie variable name).

To illustrate this example, let's perform a common task and write a script to determine whether cookies are enabled in a client browser. To do this, first we must attempt to create a cookie and then force the browser to reload the page. When the browser reloads the page, if it accepts the cookie it will automatically send that cookie back to the server. By checking for the existence of this cookie when the page reloads, we can determine whether cookies are working.

The one trick in this script is determining whether the cookie should be set and whether, for whatever reason, the browser did not accept it. To indicate to our script that the cookie has been set in the browser, we'll need to provide a GET parameter when we redirect the browser, as shown in Listing 6.6:

Listing 6.6. Checking for Cookie Support from PHP
<?php

    if(!isset($_GET['testcookie'])) {

        setcookie("testcookie", "test value");
        header("Location:  {$_SERVER["PHP_SELF"]}?testcookie=1");
        exit;

    } else {

        if(isset($_COOKIE['testcookie'])) {

            setcookie("testcookie");
            echo "You have cookies enabled";

        } else {

            echo "You do not support cookies!";

        }
    }

?>

As you can see, this script has two facets to it. The first half of the if statement is used to attempt to create a cookie and then redirect the browser to the same page with an additional GET parameter. When the script is re-executed with the additional parameter, we then test for the existence of the cookie. In the event the user does indeed have cookie support, we clean up our test cookie with an additional empty setcookie() function call.

    Team LiB
    Previous Section Next Section