State Maintenance

The most direct way in which you'll be involved with state in PHP programs is when trying to preserve variable values between page requests because each page is its own program, and once the program is done processing, all variables and values are lost—the program's done, and everything is erased out of memory. Unlike desktop applications, which maintain data in memory until you shut them down, a PHP program runs only when a page request activates it, and then only until processing is completed and/or HTML or text content is returned to the user.

PHP is able to bridge the gap between each request-response and the next, providing persistence of data—and, yes, prolonged interactivity. This process of continually updating the server with information, a major ingredient in most interactive sites, is necessary to maintain a session. Session maintenance is how you refer to a series of interactions, beginning with the user accessing or logging on to a site, and ending with the user logging out or being timed out for inactivity.

The key to this process is usually a cookie (a small piece of data stored on the client's computer) or some sort of a variable that the client and server pass back and forth, often called a session key. These cookies and keys are used to help the server know which client it is interacting with for any given request. Just as a username identifies a user to a system, a session key (or an appropriate cookie) identifies a client to a Web site.

Sessions also maintain state across page requests and you'll examine them in detail a little later. First, take a look at some other methods available to you, including the use of hidden form fields, query strings, databases, and cookies.

Hidden Form Fields

You've seen how hidden form fields work to send data (coded by the designer) back to the server when the form is submitted. If you want to maintain state using hidden form fields (for example, a unique value representing a product the user is working with, such as the product ID), you can simply place the current product ID value into a hidden form field. Of course, the idea is that you would have the user select the product from a drop-down box (the select element) and then submit the form, using something like this code:

<form action="myform.php" method="post">
<select name="selected_product_id">
<option value="121">Product 121</option>
<option value="122">Product 122</option>
</select>
<input type="submit" name="button" value="Select Product">
</form>

You could return a page to him using the following code:

<input type="hidden" name="chosen_product_id" value="<?php echo
$selected_product_id; ?>">

Before the page is returned, the user's input would be processed as follows (assuming he chose product 122):

<form action="myform.php" method="post">
<input type="hidden" name="chosen_product_id" value="122">

So the next time he submits this form, part of the data submitted would be chosen_product_id, which PHP would turn into $chosen_product_id, and which you could then use anytime you wanted to perform processing on the product he chose, no matter how many page requests ago he chose that product. Don't forget, though, you'll need to populate that same hidden form field value each time the user requests another page or you'll lose that value.

Query Strings

Query strings can be used in exactly the same way as hidden form fields to maintain state values across page requests. However, as previously discussed, their name/value pairs are displayed for all to see in the address bar of the browser, along with the URL. This is a very insecure method of transmitting data, and as you've probably guessed, easy for malicious users to break, disable, or otherwise mess with.

Databases

The mechanics of databases and database access haven't been discussed yet (you'll get to it but you're certainly aware that databases are useful for storing structured information persistently (meaning that the data is retained even if the server is turned off), so you can understand why databases are useful for storing data across page requests. Basically, if you don't mind the extra overhead of making a query on the database each time a page request occurs, you can use a table in the database to store all pertinent data that must be preserved across page requests. The downside, of course, is the overhead involved in those database connections, and the extra effort building the database in the first place.

Cookies

So what are cookies? They're a quick (and some would say messy) method of storing, on the client's computer, small snippets of data that you want to persist between separate visits to a Web site. They aren't very powerful or reliable—you certainly wouldn't want to use them for permanent data storage, because the mere act of switching browsers completely obliterates all your old cookies. But they can be very handy for a wide variety of things. Some of the most common examples of cookie use include the following:

  • Storing a user's aesthetic preferences for a specific site.

  • Storing a user key (or keys) that can be used to link users with their personal data—as used in countless shopping basket features, for example.

  • Providing a semi-permanent session key, enabling a user to remain logged on to a site until he explicitly asks to leave it or the browser closes.

Cookies are best used for small, helpful but non-critical things. One of the best examples of cookie use is to store preferences describing how a user wants your site to appear. Although the capability to customize a site's color scheme is a nice perk for users, those who can't (or won't) use cookies won't be missing much.

Intended as innocuous "helpers" for Web developers, cookies have built up a bad reputation in recent years. They are often overused (for example, to store large quantities of data which is really best kept on the server end) and sometimes even abused (to gather information on consumers without their knowledge, for instance). If used sparingly and responsibly, though, cookies can be useful in a number of situations. They tend to be most useful:

  • In situations where you know for a fact that all the visitors to your site will have cookie support enabled—on corporate and educational intranets, for example.

  • To add bells and whistles to a site—features that add to a site's appeal but aren't required to make use of it.

Messing Around with Cookies

Exact details of how cookies are implemented vary from browser to browser, but certain important points apply across the board, and these are as follows:

  • A cookie is a short piece of data that can be used to store a variable's name and value, along with information on the site from which it came and an expiry time.

  • Cookies provide client-side storage, usually held in files on the client machine's hard drive.

  • Web sites can usually only modify their own cookies.

  • They can be accessed and (if the appropriate security criteria are met) altered at will by the Web server from which they were originally sent.

When a client accesses a Web site that uses cookies, the Web server tells the client (usually a Web browser) to store away a given piece of data for later use. The client is then responsible for storing that data away. Cookie-supporting browsers accomplish this by storing the data in a file named after the site the cookie belongs to, in a directory they keep reserved for this purpose. On subsequent requests to that site, the client sends back a copy of that data—the data persists on the client side until a specified period expires, causing it to be removed from the system. This specified period is set by the server when it tells the client to create the cookie, and is basically a number of seconds for which the client should keep the cookie. If the server tells the client to set an expiry period of zero seconds, the browser should keep the cookie only until the user quits the browser application.

Because cookies are kept on the client side, they're not under the control of the server once they've been created. Users can elect to delete cookies themselves, often simply by clicking a button in their browser, or by deleting the browser's cookie files. They could also edit the contents of the files if the urge took them. Just because you wrote what's in the cookie, doesn't mean you should always expect the right data to come back!

Essentially, cookies are the server telling the client "here's something to remember; remind me of it when you come back next time." Next time could be anything from when you click that link two seconds from now to when you come back next week. That's some serious persistence! It's a little like being at a conference where delegates can be identified by their name badges for as long as they care to wear them.

Web servers send clients cookies in HTTP headers, which are sent before any HTML text. Likewise, clients send back those cookies using HTTP headers. A client knows which cookies to share with a Web site, based on the server and path the client is currently accessing. So, if you're accessing www.php.net, the browser doesn't send any cookies it received from www.wrox.com.

When a cookie is set, a server name and path name can optionally be set—this limits access to the cookie to the specified server and/or path on that server. Clients use this information to determine whether they should send any given cookie. A cookie-enabled browser generally sends any and all cookies that it thinks applicable to a given site in the headers of any given access.

Set and Retrieve Cookies

PHP, as a modern Web scripting language, comes with full support, and setting cookie variables is as simple as making a call to setcookie(). As with header(), setcookie() must be called before any HTML is printed to the client's browser because cookies are set in the HTTP headers, which must be sent before any HTML.

The setcookie() function takes six parameters, of which the first three are by far the most important: These are, in order:

  1. A string to be used as the name of the variable.

  2. A string to be used as the value of the variable.

  3. A UNIX timestamp denoting the time at which the cookie will expire.

A UNIX timestamp is simply a long integer that represents a time and date by counting seconds since midnight on 01/01/1970. You can get the current time in this form by using the function time(). If you want to set a cookie to expire an hour from now, simply specify time() + 3600 for the third parameter.

The last three parameters to setcookie() are less frequently used:

  • The path to whose files the cookie is relevant; the browser doesn't return cookies that are from inappropriate paths. For example, if you set this parameter to /my/path/number/one and accessed a page in /my/path/number/two, your browser wouldn't send the cookie. If you went back to a page in /my/path/number/one, the browser would send the cookie.

  • The domain to which the cookie applies; same rules apply as to the preceding parameter. This parameter may be useful if your Web server hosts multiple domains.

  • An integer called secure. Set it to 1 if you only want your cookie to be sent when requesting an SSL-encrypted page. (The cookie won't be stored in an encrypted form on the client's hard drive; this setting merely ensures that the cookie will be encrypted for transmission across the Internet.)

In the simplest possible situation, you could leave off these last three, so that a typical call to setcookie() might look like this:

setcookie("fontprefs", "", time()+3600);

Accessing cookies is even simpler—there's nothing to call at all! Just as it does with POST variables, PHP automatically puts cookie information in the global domain, so it's as simple to use cookie values as it is to use any other variables. For example, the value of a received cookie called fontprefs is automatically be available throughout the script as the global variable $_COOKIES['fontprefs'].

There are several ways to delete a cookie. Of course, if the client knows where to look on his machine, he can always edit or delete the files in which cookies are stored. However, it's sometimes useful to be able to get the server to delete (or "eat") a cookie, and if this is the case, there are two main options:

  • Reset the cookie's expiry time to a time in the past: setcookie("num", "0", time() -9999);, for example.

  • Reset the cookie, specifying only its name: setcookie("fontprefs");, for example.

Try It: Out Use Cookies To Store User Preferences

Start Example:

Here's a script that stores user-selected choices for font size and typeface in a cookie. On subsequent visits to the page, the cookie is examined, and the preferences stored in it remain in effect. Save the code as cookies.php:

<?php
//cookies.php
if ($_POST[type_sel]) {
   setcookie("font[type]", $_POST[type_sel], time()+3600);
}
if ($_POST[size_sel]) {
   setcookie("font[size]", $_POST[size_sel], time()+3600);
}
//We define some options for font size and typeface, and as it's now safe to
add an HTML header, we do so:
$type = array("arial", "helvetica", "sans-serif", "courier");
$size = array("1","2","3","4","5","6","7");
echo "<html><head><title>Cookie Test</title></head><body><div align='center'>";

//The following form contains a pair of listboxes, which can be used to
specify the user's preferences:
echo "<form method='POST'>";
echo "What font type would you like to use?";
echo "<select name='type_sel'>";
echo "<option selected value=''>default</option>";

foreach ($type as $var) {
   echo "<option>$var</option>";
}
echo "</select><br><br>";
echo "What font size would you like to use?";
echo "<select name='size_sel'>";
echo "<option selected value=''>default</option>";

foreach ($size as $var) {
   echo "<option>$var</option>";
}
echo "</select><br><br>";
echo "<input type='submit' value='Get Cookies'>";
echo "</form>";

//Finally, we echo out some useful information, and format it using
appropriate settings:
echo "<b>Your cookies say:</b><br>";
echo "<font ";
if ($_COOKIE[font][type]) {
   $cookie_font_type = $_COOKIE[font][type];
   echo "face='$cookie_font_type' ";
}

if ($_COOKIE[font][size]) {
$cookie_font_size = $_COOKIE[font][size];
echo "size='$cookie_font_size' ";
}
echo ">";
echo "\$font[type] = $cookie_font_type<br>";
echo "\$font[size] = $cookie_font_size<br>";
echo "</font><br>";
echo "<b>Your form variables say:</b><br>";
echo "<font ";

if ($-POST[type_sel]) {
   $post_type_sel = $_POST[type_sel];
   echo "face='$post_type_sel' ";
}

if ($_POST[size-sel]) {
   $post_size_sel = $_POST[size_sel];
   echo "size='$post_size_sel' ";
}
echo ">";
echo "\$type_sel = $post_type_sel<br>";
echo "\$size_sel = $post_size_sel<br>";
echo "</font>";

   echo "</div></body></html>";
?>

Open cookies.

How it Works

The lines of most interest happen to be the first two functional lines in the script. Remember that cookies are set in the HTTP headers, so you have to place these calls before outputting any HTML:

<?php
//cookies.php
if ($_POST[type_sel]) {
   setcookie("font[type]", $_POST[type_sel], time()+3600);
}
if ($_POST[size_sel]) {
   setcookie("font[size]", $_POST[size_sel], time()+3600);
}

An expiry time of one hour from the current system time is specified for each cookie.

A pair of arrays containing available font sizes and typefaces are defined, and list boxes subsequently put into the form use these arrays to specify all the possible options. When the form is submitted, the values chosen are posted back to your script to use:

$type = array("arial", "helvetica", "sans-serif", "courier");
$size = array("1","2","3","4","5","6","7");
echo "<html><head><title>Cookie Test</title></head><body><div align='center'>";

//The following form contains a pair of list boxes, which can be used to
specify the //user's preferences:
echo "<form method='POST'>";
echo "What font type would you like to use? ";
echo "<select name='type_sel'>";
echo "<option selected value=''>default</option>";
foreach ($type as $var) {
   echo "<option>$var</option>";
}
echo "</select><br><br>";
echo "What font size would you like to use? ";
echo "<select name='size_sel'>";
echo "<option selected value=''>default</option>";

foreach ($size as $var) {
   echo "<option>$var</option>";
}
echo "</select><br><br>";
echo "<input type='submit'>";
echo "</form>";

Finally, the cookies and the posted form variables are echoed out to show how their values change as selections are made. The variables displayed are formatted with their respective values:

//Finally, we echo out some useful information, and format it using
appropriate settings:
echo "<b>Your cookies say:</b><br>";
echo "<font ";
if ($_COOKIE[font][type]) {
   $cookie_font_type = $_COOKIE[font][type];
   echo "face='$cookie_font_type' ";
}

if ($_COOKIE[font][size]) {
   $cookie_font_size = $_COOKIE[font][size];
   echo "size='$cookie_font_size' ";
}
echo ">";
echo "\$font[type] = $cookie_font_type<br>";
echo "\$font[size] = $cookie_font_size<br>";
echo "</font><br>";
echo "<b>Your form variables say:</b><br>";
echo "<font ";

if ($_POST[type_sel]) {
$post_type_sel = $_POST[type_sel];
echo "face='$post_type_sel' ";
}
if ($_POST[size_sel]) {
$post_size_sel = $_POST[size_sel];
echo "size='$post_size_sel' ";
}
echo ">";
echo "\$type_sel = $post_type_sel<br>";
echo "\$size_sel = $post_size_sel<br>";
echo "</font>";

   echo "</div></body></html>";
?>

Nothing too tricky there. When you run cookies.php for the first time, both your form variables and your cookie variables are empty. If you make a selection from each of the list boxes and click the Submit Query button, you'll fill the form variables with values, and these are echoed to the screen.

So what does this tell you? The form variables type_sel and size_sel have now been updated with the inputs selected in the preceding form. Note that the cookies are both still empty—don't worry, they're present all right; all you need to do now is refresh the page.