Web Development
Using PHP

Simpson College
Computer Science

Sessions and Redirects

We want to be able to process forms to allow a person to log in or perform other actions that are common with forms. Before we do this, we need to learn about two things. We need to learn about sessions and we need to learn about redirects.

Why do we need sessions?

The web is stateless. That is, unless we add some extra help, each web request to the web server appears unrelated to prior web requests. You can tell your name to the web server, and then you can ask for a different page not one second later and the web server will have no idea who you are.

Clearly something exists that gets around this limitation. Otherwise we would not be able to log into a computer.

One of the first thoughts that comes to many students is to use the client's address to identify him or her. So if a client named “Bob” logs in from 127.0.0.1, we know that every other request that comes 127.0.0.1 must come from Bob.

Unfortunately it isn't that simple.

With the use of technologies like Network Address Translation (NAT) there could be hundreds or thousands of computers sharing one Internet address. Or “Bob” could log out of his computer, and then “Sam” sits down a couple minutes later at that same computer and then sees Bob's account. This isn't any good.

We need a way to establish a session. This will allow all web requests from the same person on a web browser to be grouped together and have data from one web request persisted to another web request.

Why do we need redirects?

A common pattern when programming web pages is to have a set of web pages for displaying, and a set of web pages for processing. This separation of display and processing is common in not just web development, but many other paradigms as well. If you have done any GUI work, you have probably heard of the Model View Controller (MVC) pattern. Many web sites also follow this pattern. As you get into larger websites and multiple applications that share code, the choice to do MVC is a no-brainer.

Here is an example of how this works in the web-world:

fig.redirect
Redirection With Forms

The example above has two web pages for display:

There is one page for processing output:

There are two headaches here. One, the form_process.php may not display anything to the user. Is there an error with the form? The form_process.php will save the error, redirect the user back to form_.php, and form_process.php will display the error.

The second headache? Remember the web is stateless. Once I send a user back to form.php I have forgotten everything about her and I don't remember any error.

First, we will learn how to do redirects. Second, we will learn how to not forget about our users.

Redirects:

Why use redirect and not include?

When a program is done processing a form it can do an include command for the next page:

if ($error == FALSE) {
	include("next.php");
} else {
	include("form.php");
}

The problem with this is that if the user bookmarks the resulting URL, it will not be for index.php, but instead for the login_process.php. Then the user will arrive at a page different than expected, and the form will process with no form information data giving the user lots of angry messages.

Ok, how do I use redirect?

The other way to do it is by the use of a redirect. Instead of directly including more php code, the application server sends a short message saying "Go to this page instead." The browser will get that response and make a second request from the new page.

if ($error == FALSE) {
	header("Location: next.php");
} else {
	header("Location: form.php");
}

A 'redirect' will send a message back to the client web browser. The client web browser will not display anything, but will look at the message and see that it has been redirected to another web page.

As a reminder (or not, if you haven't figured this out) each web response has certain codes. A "200" is a success code. A "404" is a page not found. A "302" is a redirect.

The actual data that goes from the web server to the client looks like this trace:

fig.redirect_packet
Redirection Packet

The green area is the “header” of our HTTP response. The yellow area separates the header from the HTML. The HTML all goes in the purple area. Since this is a redirect, there is no HTML.

This is also why you can't redirect after you've printed any HTML. Once you've printed HTML, then the header has already been written out. You can't go back in time and change the header.

An example set of forms with redirect

The following code has a form very similar to what has been used before. But there is a problem. If the user makes a mistake, the $messages variable that is set in form_process.php will not be set after the redirect to form.php.

<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>Sample PHP</title>
</head>

<body>
  <h1>Form Test</h1><?php
  if(!empty($messages)) {
          foreach ($messages as $message) {
              echo $message;
          }
  }
  ?>

  <form action="form_process.php" method="get">
    <!-- Enter name -->
    First name: <input type="text" name="firstname" <?php
	if( isset($firstname) ) {
		echo 'value="';
		echo htmlentities ($firstname);
		echo '"';
	}
	?>/><br />
    <!-- Add submit button -->
    <input type="submit" value="Submit" />
  </form>
</body>
</html>
<?php
/* Keep track if there is an error or not */
$error=FALSE;

/* Validation of first name */
if(empty($_REQUEST['firstname'])) {
	/* There was no first name, that's odd */
	$error=TRUE;
	/* Create an array of messages to display the user */
	$messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>";
} else {
	/* Get the first name from the request */
	$firstname = $_REQUEST['firstname'];
	/* See if the first name matches our not-very-good filter */
	if (!preg_match("/^[A-Za-z]{1,25}$/", $firstname)) {
		/* No match, display an error */
		$error=TRUE;
		/* Create an array of messages to display the user */
		$messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>"; 
	}
}

/* If there was an error, include the form again. Otherwise continue on. */
if($error==FALSE) {
	/* Instead of doing an 'include', do a redirect */
	header("Location: next.php");
} else {
	/* Instead of doing an 'include', do a redirect */
	header("Location: form.php");
}
?>
<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>Sample PHP</title>
</head>

<body>
  <h1>Form Submitted</h1><?php
  echo "Hello $firstname."
  ?>
</body>
</html>

Try it out: [link]

Keep in mind it doesn't work though.

Cookies

By default, each web request is "stateless." Without doing some extra work, there is no way to tell that the second page that "Bob" requests is in any way related to the first request he made. This makes any interaction, even as simple as a log-in request, a problem.

One way to get around this is to use a "cookie." The concept of using a cookie may be explained by your instructor, or get even more detail by reading up on it here:
http://en.wikipedia.org/wiki/HTTP_cookie

A client may look at his or her cookies by using the web browser.

fig.firefox_cookies1
Cookies in Firefox
fig.firefox_cookies2
Cookies in Firefox

Sessions

To start a session:

session_start();

This line must be ahead of any references to session variables. It may only be called once per web request.

To set a session variable:

For example, this counts and displays the number of times a user has viewed a page within this session:
<?php
session_start();
if(isset($_SESSION['views']))
    $_SESSION['views'] = $_SESSION['views']+ 1;
else
    $_SESSION['views'] = 1;

echo "views = ". $_SESSION['views']; 
?>

Try it out: [link]

The next example uses the session to save and retrieve the values from the form. This is more complex than the handling of variables in the prior examples, but it has several advantages. The user may bookmark the form, even after a failed attempt at the form, and not worry about bookmarking form_process.php instead of form.php. If the user bookmarks after the form is successfully filled out, he or she will bookmark the desired next.php rather than form_process.php. Another advantage is that $_SESSION['firstname'] will be available for all furture page requests from the user.

<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>Sample PHP</title>
</head>

<body>
  <h1>Form Test</h1><?php
  session_start();
  if(isset($_SESSION['messages'])) {
	$messages = $_SESSION['messages'];
	$form = $_SESSION['form'];
	unset($_SESSION['messages']);
	unset($_SESSION['form']);
  }
  if(!empty($messages)) {
          foreach ($messages as $message) {
              echo $message;
          }
  }
  ?>

  <form action="form_process.php" method="get">
    <!-- Enter name -->
    First name: <input type="text" name="firstname" <?php
	if( isset($form['firstname']) ) {
		echo 'value="';
		echo htmlentities ($form['firstname']);
		echo '"';
	}
	?>/><br />
    <!-- Add submit button -->
    <input type="submit" value="Submit" />
  </form>
</body>
</html>
<?php
session_start();

/* Keep track if there is an error or not */
$error=FALSE;

/* Validation of first name */
if(empty($_REQUEST['firstname'])) {
	/* There was no first name, that's odd */
	$error=TRUE;
	/* Create an array of messages to display the user */
	$messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>";
} else {
	/* Get the first name from the request */
	$firstname = $_REQUEST['firstname'];
	/* See if the first name matches our not-very-good filter */
	if (!preg_match("/^[A-Za-z]{1,25}$/", $firstname)) {
		/* No match, display an error */
		$error=TRUE;
		/* Create an array of messages to display the user */
		$messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>"; 
		$form['firstname']=$firstname;
	} else {
		/* First name is good! Save to the session. */
		$_SESSION['firstname'] = $firstname;
	}
}

/* If there was an error, include the form again. Otherwise continue on. */
if($error==FALSE) {
	header("Location: next.php");
} else {
	$_SESSION['messages'] = $messages;
	$_SESSION['form'] = $form;
	header("Location: form.php");
}
?>
<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>Sample PHP</title>
</head>

<body>
  <h1>Form Submitted</h1><?php

  session_start();
  $firstname = $_SESSION['firstname'];
  echo "Hello $firstname."
  ?>
</body>
</html>

Try it out: [link]

You are not logged in. Log in here and track your progress.