Cookies: Tasty and Useful


Chapter 13, "Web Applications and the Internet," mentions that HTTP is a stateless protocol that has no apparent way to track individual visitors or users. The solution for this is cookiesa mechanism by which the server can send a tidbit of information back to the client with a response. This tidbit consists of a name and a value that are sent as text strings. When the client makes another request of the server, it sends back any cookies it has for that server along with the new request.

Basic Operation

We will begin by looking at how to set and access cookies in PHP scripts.

Setting Cookie Values

Setting cookies in PHP requires the use of only one function: setcookie.

 setcookie(cookie_name, cookie_value); 

The setcookie function escapes the cookie value you give it so that it can be safely sent as part of an HTTP response message. The cookie name parameter only consists of alphanumeric characters (although some special characters, such as underscores (_) and dashes(-), are permitted). The resulting cookie lasts as long as a client browser is open, and it is deleted when the browser is closed.

The one problem with calling setcookie is that (like the header function) no output can come before this function is called. This is because cookies are sent as part of the response header; if you send any output to the client, PHP quietly sends the headers before beginning the output stream. This can be somewhat tricky to prevent since even a single blank line or space character (that is not part of a PHP code block) in any part of the scriptor in any of the include files from within the scriptcan cause output to be sent.

Consider the following snippet of code. (Line 2 is simply a blank line.)

 <?php /* file1.php begins now */ ?> <?php  setcookie('loaded_okay', 'TRUE'); ?> 

When you run this on your browser, you see the following output:

 Warning: Cannot modify header information    headers already sent by (output started at   c:\documentroot\phpwebapps\setcookie.php:3) in   c:\documentroot\phpwebapps\setcookie.php on line 3 

That single blank line is all it takes to cause these problems, so we must be extremely careful not to do this in our include files and code files before we have finished setting any cookies. Fortunately, a more robust and less stressful solution exists in the form of output buffering, which we will see in Chapter 21, "Advanced Output and Output Buffering."

Accessing Cookie Values

When the client browser sends a cookie back to the server with a request, you can access its value by using the $_COOKIE superglobal array in PHP. $_COOKIE is also available as $HTTP_COOKIE_VARS if register_long_arrays is set in php.ini, and the individual cookie values are available as global variables in PHP if register_globals is set in php.ini. However, both are discouraged in favor of the more modern $_COOKIE.

 <?php   echo $_COOKIE['loaded_okay'];  // outputs TRUE ?> 

PHP only populates the $_COOKIE array with cookies sent with a request to the page. Thus, if we execute the following code before any other script has called setcookie('loaded_okay', '...')

 <?php   setcookie('loaded_okay', 'TRUE');   echo "{$_COOKIE['loaded_okay']}";   // not ok: not set yet!! ?> 

we receive a warning saying that the loaded_okay key in the $_COOKIE array has not been set. This array is only accessible after a new page is loaded (see Figure 19-1).

Figure 19-1. Requests, responses, and cookies.


Similarly, $_COOKIE is not updated with new cookie values right after we set them in code. It is only filled with cookie values that are received with the incoming request. Assuming that we have a request that sends us the loaded_okay cookie.

 <?php echo "{$_COOKIE['loaded_okay']}";   // outputs TRUE initially setcookie('loaded_okay', 'banana'); // set a new value. echo "{$_COOKIE['loaded_okay']}";   // still outputs TRUE! ?> 

we see that the new value, 'banana', is not actually available in the $_COOKIE array until we begin processing a new page.

A Cookie Example

A more interesting example of how cookies work would be the following code, which puts up a form asking the user to select a color:

 <?php if (isset($_GET['color'])) {   setcookie("user_colour", $_GET['color']);   echo <<<EOM   Cookie set to selected value: <b>{$_GET['color']}</b><br/> EOM; } else {   echo <<<EOM   No Color was sent with the request.  Pick a color   to set.<br/> EOM; } if (isset($_COOKIE["user_color"])) {   echo <<<EOM   The cookie value received with the request was:    <b>{$_COOKIE['user_color']}</b><br/> EOM; } ?> <form action='<?php echo $_SERVER['PHP_SELF'] ?>' method='GET'>   <font color='red'>   <input type='radio' name='color' value='red'/>Red<br/>   </font>   <font color='blue'>   <input type='radio' name='color' value='blue'/>Blue<br/>   </font>   <font color='green'>   <input type='radio' name='color' value='green'/>Green<br/>   </font>   <font color='yellow'>   <input type='radio' name='color' value='yellow'/>Yellow<br/>   </font>   <font color='orange'>   <input type='radio' name='color' value='orange'/>Orange<br/>   </font>   <input type='submit' value='Submit Choice'/> </form> 

The main part of the visual page is the HTML form at the end, which simply lets you select a color. After you have done so, it calls the same page again (hence the reference to $_SERVER['PHP_SELF'] as the page to which the form should go). The code at the top of the page:

  • Looks to see if a color value was submitted to this page via HTTP GET. If one was received, it calls setcookie to send the color along with the response.

  • Looks at the $_COOKIE superglobal array to see if the user_color cookie was sent with the request. This is not true until after setcookie('user_color',) has been called and the page has been reloaded (by clicking on the Submit button).

Thus, the first time you select a color and click "Submit Choice," you see that the color parameter was sent via an HTTP GET request, but no cookie was sent with the request. The second time you click on "Submit Choice," a cookie is sent along with the request (see Figure 19-2).

Figure 19-2. Setting colors and cookies.


You are encouraged to play around with the preceding example since it shows the distinctions between incoming parameters (GET or POST), incoming cookie values, and the lifetime of values in the $_COOKIE array.

How Cookies Work

The server sets a cookie in the client by sending a Set-Cookie header along with the response. (Multiple cookies require multiple headers.) For example, consider again the color selecting pages shown in the "Basic Operation" section. When the user clicks the "Submit Choice" button after selecting a color, his browser sends an HTTP GET request to the server. The script then sends the following response headers. (We will omit the body for brevity.)

 HTTP/1.x 200 OK Server: Microsoft-IIS/5.1 Date: Fri, 07 Oct 2005 18:38:55 GMT X-Powered-By: ASP.NET, PHP/5.0.2 Connection: close Content-Type: text/html Set-Cookie: user_colour=green; path=/ 

The next time the user clicks the "Submit Choice" button, the request looks like this:

 GET /setcookie.php?colour=blue HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3)     Gecko/20040913 Firefox/0.10.1 Keep-Alive: 300 Connection: keep-alive Referer: http://phpsrvr/setcookie.php?colour=green Cookie: user_colour=green 

Even though multiple cookies are sent with individual Set-Cookie headers in the response, they are all sent back to the server in a single Cookie header in requests with each cookie name/value pair separated by semicolons:

 Cookie: user_colour=orange; animal=moo+cow 

Controlling Cookie Validity

The setcookie function accepts up to six parameters instead of just the two we previously saw.

 setcookie(name, value, expires, path, domain, secure) 

The rest of the parameters are useful for controlling and restricting the availability of the cookie you set.

The expire parameter lets you control the duration of the cookie's validity. It specifies the expiration date in seconds since midnight, January 1, 1970. (You can call the time function to find out the current time in seconds since that date.) For example, to specify that a cookie must expire in one hour (there are 3600 seconds in one hour), you might write

 setcookie('one_hour', 'still good', time() + 3600); 

After a cookie has expired, the web browser no longer sends it back to the server as part of any requests. A value of 0 for this parameter indicates that this should be considered a session cookie. It is stored in memory instead of on disk, and it is valid only as long as the user is browsing. After the user closes down his web browser, the value is lost.

A NOTE ABOUT BROWSERS AND SESSION COOKIES

Many modern web browsers are multi-threaded, with each new browser window being a new thread within the same master browser process. This helps them be a bit faster and share many of the same resources, such as memory. Thus, even if you close a window associated with a session cookie, it is entirely possible that other windows for the same master browser process are still holding that cookie in memory. This is why some sites tell you to "close all browser windows" when logging outonly then can you be sure that the session cookies are truly gone.


The path parameter lets you restrict the pages for which the cookie is valid. For example, if you set the path parameter to '/', the cookie is valid for all pages in the site; however, if you set it to '/admin', then it is only valid for the URLs on the site beginning with /admin. If it is not specified, the default value is the directory in which the cookie is being set.

The domain parameter lets you restrict or expand the set of machines for which the cookie is valid. By default, it is valid for the server from which it was sent. However, if you have a large series of servers with names like www1.example.com that are sharing the web application load, you might want to set this to '.example.com', which says that the cookie is valid for any machine within the domain.

Finally, the secure parameter merely lets you state that a cookie is valid only over HTTPS connections with value 1. It defaults to allowing them over both secure and insecure connections with the value 0. This does not change the fact that the cookies are stored as plain text on the user's computer.

Deleting Cookies

At some time, you may no longer need a cookie and wish to get rid of it. One option is to leave it on the client machine to expire. Unfortunately, this is probably not a good idea. Most cookies are stored on client machines in an easily read text file; if the user was on a public machine at an Internet café, any information in the cookie could be read by the next user.

It is better to explicitly delete the cookie. Doing this in PHP is trivial. You merely set a new value in the same cookie with an expiration date in the past:

 setcookie('loaded_okay', '', time()  3600); 

This causes the client to realize that the cookie has expired and leads the program to clean it up.

Cookie Arrays

PHP has a convenient mechanism through which you can associate arrays with cookies. For example, to save a user's username, address, and birth date in a cookie array called UserInfo, we could write the following code:

 setcookie("UserInfo[name]", $_POST['user_name']); setcookie("UserInfo[address]", $_POST['address']); setcookie("UserInfo[birthdate]", $_POST['birth_date']); 

When we next access the cookie (in a new page to which this data was sent back), there will be one member in the $_COOKIE array that is itself an array that contains the data:

 $name = $_COOKIE['UserInfo']['name']; $addy = $_COOKIE['UserInfo']['address']; $bday = $_COOKIE['UserInfo']['birthdate']; 

PHP sends one cookie for each value (since they have different names), but it is smart enough to create an array for you when it is loading the cookie values.

What to Place in Cookies

Knowing what to put in cookies is as important as learning how to use cookies. Although cookies are an extremely convenient mechanism through which to associate information with a particular client, they have a number of drawbacks. Most notably, they are transmitted over the Internet and stored on the client computer in insecure plain text format. In addition, there are limitations on the amount of data that a cookie may contain (often in the range of four kilobytes).

Thus, given that we cannot safely put credit card numbers and high-resolution images of our pet cats in the cookies, we must choose what is appropriate. Cookies are most commonly used to put a user identifier in so we can access the user tables in our database and identify a user who comes back to the site. We cannot trust this information much since it can be forged by an attacker, but it is not a gross security violation to use it to personally welcome the user back to the site and ask the user if he would like to log in.

Other data, such as user preferences, are great for placing in cookies. If your site is content-centric and you want to let users customize fonts and colors, it makes more sense to put this information in cookies instead of a database table, which would require an entire login and user management system.

As a general rule, we will err on the side of not placing too much information in cookies, but rather use them as tokens to help us manage the user. When we want to store information on the user's machines, we will work to make sure it is largely harmless and not likely to create a security hazard.

Weight Watchers

The wave of security and privacy concerns on the Internet has led to a number of solutions to security problems, including browsers that permit the user to refuse to accept cookies from some or all web sites. Although it helps the user, it can make your life as a web application author more difficult. To deal with this, we need a way to detect whether the user accepts cookies.

The following script is a good example of how we might go about this:

 <?php if (isset($_GET['cookies_detected'])     && $_GET['cookies_detected'] == '1') {   //   // we called setcookie before reloading the page. Did it work?   //   if (isset($_COOKIE['cookie_test'])       && $_COOKIE['cookie_test'] == 'cookie_test')   {     $cookies_enabled = TRUE;   }   else   {     $cookies_enabled = FALSE;   } } else {   //   // set a cookie with the client and then reload this   // page, seeing if we have the cookie.   //   setcookie('cookie_test', 'cookie_test');   header("Location: {$_SERVER['PHP_SELF']}?cookies_detected=1"); } ?> 

The script works by trying to set a cookie and then calling itself again with a GET parameter that says it has tried to set it. When the page loads again and sees the attempt, it looks to see if the cookie was sent back from the client. If not, we can assume the cookies are disabled and print a message. Otherwise, we can assume they work.

Web application authors are encouraged to make sure that cookies are a necessary part of their systems before they require users to enable cookies to visit the site. Bombarding the user with cookies in seemingly random places can make the user suspicious of your application and cause him to avoid it.




Core Web Application Development With PHP And MYSQL
Core Web Application Development with PHP and MySQL
ISBN: 0131867164
EAN: 2147483647
Year: 2005
Pages: 255

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net