HTTP is stateless. When you make a request, the request is sent
to the server and then the connection is terminated. When the response is
sent back, it is sent from the server to the client, and the connection is
again terminated. HTTP doesn't "remember" anything about previous requests
and responses: When you click a link on a page, the server doesn't remember
any of your previous requests or information about those requests.
This is unlike FTP (File Transfer Protocol, which you might have
used if you've ever uploaded/downloaded files using a client such as FileZilla
or CoreFTP), which is stateful.
FTP maintains the connection between the
client and the server and keeps track of state information about the connection.
The problem with a stateless protocol like HTTP is that you (as a developer)
often need to keep track of information about the user/client between
requests. For example, if your site is an online store, you need to keep
track of what items the user adds to their shopping cart as they navigate
from one page to another. Each time the user requests a new page, how does
the server "remember" what items the user already had in their cart?
One way to remember this kind of information is by using
cookies. Cookies are permanent but they can
only
store small amounts of text data. Additionally, many more savvy users
block cookies in their browser, as a privacy measure.
Another option many PHP developers prefer are sessions.
A session is a visit to a web site or application:
A session consists of
a user first loading your site/application, using and/or navigating around the
site/application, and then leaving the site/application.
A Session begins when a user accesses your application (any page,
generally). A Session ends when one of the following happens:
The session times out. A session times out after 30 minutes of
no activity, when there are no new requests made with a
specific session ID. The timeout value of 30 minutes can
be changed programmatically in your application.
Your code invalidates or destroys the session. For exmaple,
you might write code that destroys the session when a
user logs out.
The application or server shuts down or restarts.
When a Session is first created, a unique session ID
is generated for that session. The server stores the session ID along
with any other user data that was included in the request. The
server then sends the session ID back to the client in the response.
The client reads the session ID and stores it in
as a session cookie.
For the next request, and all subsequent requests, the session id
stored in the session cookie is sent along in the request object.
The server then reads the session ID in the request
and matches it to table of session data it has: the session data
with the same session ID belongs to that specific user during this
current session. Any new data can be added and associated with that
same session ID as neeeded.
When the session expires or is terminated, the server deletes the stored
session data and releases the session ID and its resources. After this
occurs, the server doesn't remember the user or any of the stored data.
A developer can use a session variable to keep track of information as the user
navigates around the site, making different requests of the server.
For example, as the user shops around on your store's site, session variables
can be used to remember the items in a shopping cart. When the user navigates
to a new page, that page is requested using a new HTTP connection: the page's
code can use a special session ID to add, read, and edit
session variables stored on the server.
Sessions can somtimes be more advantageous than regular cookies:
Cookies are stored on the
user's computer, and many users not wishing to have their privacy violated
will configure their browser to refuse cookies but still allow
session cookies (although you will still have to add fallback code if
the user has chosen to block all cookies).
Additionally, cookies can only store text data
and to a maximum of 4096 bytes (that includes the cookie name, expiry date,
the cookie's value, etc). PHP Sessions don't have a maximum size, and
can contain text, numbers, arrays, or entire objects. However, the server
running the PHP scripts does have a configuration option called memory_limit,
which defines the maximum amount of memory allocated to a running script;
the default value is 128MB so clearly you wouldn't want to store
anything in a session that's close to 128MB in size.
In PHP, session information is stored in the global variable called
$_SESSION. $_SESSION is an associative
array where keys reference objects and values that you can add, edit,
and remove.
You programmatically start a session when the user visits one of your
pages, and the session ends when they leave your page, although you can
also programmatically remove data from the session and/or end the session.
Creating a Session
The first thing that you need to do if you want to store and
access session data is begin a session. This needs to be done on every page
of your site that is going to use the session data.
To start a session, we invoke the session_start()
function. The session_start() function will:
check to see if a session already exists:
If the session exists, the session is resumed and your
code can access any of the stored session data and add new
session data.
If the session doesn't exist, a new session is started: a
new session ID is generated by the server.
The session_start() function call must be
the first line of PHP code
in your page, and must even go above the HTML <!DOCTYPE> declaration.
Furthermore, the call to session_start() must be in every single page on
your site that uses the session variables.
Each session is assigned a unique Session ID. This identifies
each session as unique to a single user. session_start() checks to see
if a session with a specific session ID already exists or not. It generates
a new session ID if it needs to create a new sessions.
When a session is created and when data is added to the
session, the $_SESSION global associative array is automatically
populated with any session data stored on the server.
Adding and Reading Session Data
Once you have started or resumed a session, you can add data to it.
You simply make up a key and store your data as the value in the $_SESSION
variable. You can then read it back using the same key:
When you load this page, you'll see an error on your page similar to
the one below (obviously my path and line # might be different from yours):
In an earlier lesson, we learned the isset()
function. We can use that to see if a session variable was set or not.
If a session variable was not set, it means it doesn't yet exist (perhaps
this is the user's first visit in the session).
In the code above, we've added a line of code (line 4) that checks to
see if $_SESSION["example2"] exists. If it does, we store that session
variable in the scalar variable $ex2. If the $_SESSION["example2"] session
variable doesn't exist, we store the string literal "no data set" in the
$ex2 variable. When you load the page, you'll see "no data set" echoed
onto the screen, because we've never created the example2 session variable.
Try adding some code at the bottom of the page that assigns a value
to the example2 session variable, then reload the page and see if that
value outputs onto the screen.
You can modify a session variable any time using an assignment
statement, e.g. $_SESSION["example1"] = "Bye";
Exercise/Demonstration
Try this demonstration and exercise to help you understand how
session work in PHP. We'll store a variable called "username"
in the session, but we'll also use a form to store a "username"
variable in the query string, too.
Create a new project with two files:
index.php and page2.php.
Copy and paste the following content in each:
Once you've copied and pasted the code, let's start completing the
various TODO's that have been left for you:
Add the session_start() function to both pages.
Sessions will not work unless the session_start()
function is called at the top of each page that requires
access to the $_SESSION[] array and other session functions.
Upload your project and test out the index page: View the
cookies for this page: you'll see a cookie has been
set under the domain you're using to test your
project. If you're able to view the cookie, you will see
it's called PHPSESSID. If you're able to
view its value, you'll see it's a long string of what
looks like random characters: that's the session ID!
If you delete the cookie, and then reload the page, you'll
notice that it is re-created with a different ID! When you
added session_start(), it looked for a session ID in the request.
When it doesn't find one, it creates and new one and sends it back
in the response, causing your browser to create a new session
cookie with the session ID.
Add the PHP code that displays the "Thank you" paragraph
only if there's a "username" variable in the $_SESSION[].
If there isn't, display the form. Use "alternate syntax": it's
easier to edit!
Upload and test your page: you should see the form, but
no paragraph when you visit the index page.
Add the if-statement near the top of the page that adds
the "username" to the $_SESSION if it exists in the query string.
There's no action to perform if the "username" doesn't
exist in the query string: this is a single-sided if.
Re-upload and test your index page: Enter a name in the
text box and click the SUBMIT button. You should see
the paragraph instead of the form!
Add the code to display either the username from the $_SESSION
or the word "Guest" in the level-2 heading. Then add the code
that displays the value of the "username" variable from the query
string in the first paragraph (if it doesn't exist in the query string,
display "no username in get"), and the value of the "username"
variable from the session in the second paragraph (if it doesn't
exist in the session, display "no username in session"). You can
copy the paragraphs into page2.php where you see step 4c.
Make sure you use isset() - if you try to refer to a
variable that doesn't exist in either associative array,
you'll get an error. You can use the conditional operator (?:)
in a PHP echo tag: it's easier!
Upload both pages to the server.
Delete the session cookie before you reload the index page!
Make sure there's no query string in the URL when you reload!!!
After reloading, You'll see "Guest" in the welcome heading,
you'll also see that there is no "username" in the query string
or the session, and you'll see the form.
Enter a name and press SUBMIT: you'll see the username in
the welcome heading, and you'll see the username in the
paragraphs. You should not see the form.
Click on the Page 2 link: you should see no value for "username"
in the query string, although you'll see the username in the
session. Why do you not see the username in the query string?
If you need to, you can remove session variables so that they no longer
exist. To remove a session variable, use the unset()
function. Pass the function the session variable you want to unset:
unset($_SESSION["example1"]);
To unset all of the session variables, use the session_unset() function:
session_unset();
If you want to end a session, use the session_destroy() function:
session_destroy();
This will destroy the session data for the current session.
However, this won't destroy the session cookie on the client machine
and won't necessarily destroy any global session variables.
If you want to destroy a session, a better way is to simply
delete all the session data manually:
$_SESSION = array();
This still won't delete the session cookie, but that's fine.
If you really want to destroy the cookie, check the
PHP Manual entry for
session_destroy() for
some code that deletes the session cookie.
Note that a session will automatically destroy itself a certain
amount of time after the user closes the browser, navigates away
from your site, or remains inactive for a period of time. You don't necessarily
have to end the session unless you want to make sure it ends while the
user is still on your site. The amount of time it takes a session to
end automatically depends on the server settings: there
is an option called "session.gc_maxlifetime" that sets the
amount of time a session should stay alive.
Login Demonstration
Try this Login Form Demonstration: it will show you
a practical use of sessions and also show you how
to log out a user and destroy and existing session.
Copy the code below into a new project:
<?php
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// **NEVER store plain text password data EVER, this is a demo only**
// username => password
$pretendUserData = [
"foobar" => "password",
"wendi" => "tunafish",
"arti" => "iamboss"
];
// stores potential error messages
$errorMessages = [];
// get username and password from query string
$userName = filter_input(INPUT_POST, "username", FILTER_SANITIZE_SPECIAL_CHARS);
$password = $_POST["passwd"];
// if there's no username, that's an error
if (!$userName) {
$errorMessages[] = "User name is a required field. Please enter your username.";
}
// if there's no password, that's an error
if (!$password) {
$errorMessages[] = "Password is a required field. Please enter your password.";
}
// if we have no error messages so far, see if credentials are valid
if (!$errorMessages) {
// it's an error if this user doesn't exist in our pretend database
if (!userExists($userName)) {
$errorMessages[] = "Sorry, you are not in our database. Register for an account.";
// it's an error if the password for this user doesn't match what
// we've stored in our pretend database
} else if (!correctPassword($userName, $password)) {
$errorMessages[] = "Invalid username or password.";
}
}
// if there are any errors, go back to login page
if ($errorMessages) {
redirectToLogin();
// if there are no errors, move on to welcome page
} else {
redirectToWelcome();
}
function redirectToLogin() {
// back to login page
// TODO: UPDATE YOUR url
header("Location: https://fill/this/in/loginForm.php", true, 302);
exit();
}
// it's tempting to turn redirectToLogin() and redirectToWelcome() into
// one re-usable function, which would be correct, but these two methods
// will be doing different things later so just leave it this way
function redirectToWelcome() {
// go to welcome page
// TODO: UPDATE YOUR url
header("Location: https://fill/this/in/welcome.php", true, 302);
exit();
}
// returns true if the specified $user exists in our
// pretend user data
function userExists($user) {
global $pretendUserData;
return array_key_exists($user, $pretendUserData);
}
// returns true if the specified $user's inputted $pass matches
// what is stored in our pretend user data
function correctPassword($user, $pass) {
global $pretendUserData;
return $pretendUserData[$user] === $pass;
}
?>
Have a look through the code in each file before continuing.
You'll notice I used the header() function in the
login.php file. The header() function is used
to redirect a request to a different page. Here's how a redirect
works:
Your browser sends a request, let's say the request is
to foo.php.
The code inside foo.php contains header("bar.php"),
so the server sends a response back to the server with
either 301 (Moved Permanently, used when the requested
resource has been permanently moved to a new file/location)
or 302 (Found, similar to 301 except the change is temporary)
along with the new file name to request (bar.php).
The client initiates a new request for bar.php
The server receives the request, processes it, and returns
bar.php in the response.
The header() function accepts three arguments:
The HTTP header you want to send.
An optional boolean true if you want this header to replace
a previous header with the same name, and false if you want to
add this header in addition to one that already exists (it's
common for some types of headers to have more than one).
This is true by default.
An optional response code to use instead of the standard 200 OK.
To use header() for redirecting, just set the
Location: header to the URL you want to redirect to.In my example, I just used
header("Location: https://.../loginForm.php").
This sends the a response to the client saying that I want it to
make a new request to the specified URL.
After you call header(), you must call either the
exit() or die() function immediately
after. This stops the script from executing any further. Failure
to call either exit() or die() after
header() will cause problems.
NOTE that you can't send any output (e.g. echo) before you call
the header() function, otherwise you'll see the
"headers are already sent" error message.
Upload all three files to your server and test them out
by loading the loginForm.php page.
Try clicking the SUBMIT button without entering any credentials,
and also try it after entering incorrect credentials. You will
not see any error messages, even though you can see that your
code is validating and recording error messages: we haven't
displayed these on the screen yet. You can see I've left a
spot for them in the loginForm.php page's code.
Now try logging in with a set of correct credentials (the list
of credentials is in the $pretendUserData array in
the login.php file).
Notice that after logging in, you only see the Welcome
heading, and nothing else: eventually we'll add some more
content here that includes the user's name.
Let's modify the example to make it use sessions so we
can display error messages and the user's information.
Add the code to each of the 3 files to initiate/continue
a session. Use session_start(). Go back through
your notes if you can't remember where it goes.
When there are errors, we are redirecting back to the loginForm.php
file. We would like to be able to display the error messages on
that page:
Add a statement above the header()
function inside redirectToLogin()
that stores the array of error messages in a session variable
called "errorMessages". You'll need to also pass the array
of error messages into the function.
On the loginForm.php page, add the
code that displays all the error messages inside the marked
<div> element (the one right above the form). Make sure
you check to make sure the "errorMessages" variable exists
before iterating through it.
A this point, you can upload your files and test out the
login again: try no credentials, a bad username, and a correct
username with a bad password. You should see error messages
on the page above the form.
When the user successfully logs in, we can display their
username on the Welcome page. This is very similar to what
we did in the previous demonstration, so this should be simple:
In the redirectToWelcome() function,
add the statement that adds the username to the "username"
session variable. You'll have to pass the username into the
redirectToWelcome() function.
On the welcome.php page, display any kind of greeting
to the user (using their specific username) in a level-2 heading
inside the <main> element.
Re-upload your files and test your changes, although you
should delete the session cookie and then restart with
a correct login to avoid any problems. Once you log in,
you should be presented with the Welcome page showing
your username that you used to log in.
Now let's add a Log Out function to our site. Let's add a
log-out link to the Welcome page. When the user clicks the
link, we'll call a logout.php file:
<p><a href="logout.php">Log Out</a></p>
Now add the logout.php file to your project:
<?php
// we need this so we can access the current session
// and its data
session_start();
// clear the session array
$_SESSION = array();
// add errorMessages with one element:
// why? we're going back to the loginForm page, so
// this will display in the DIV above the form, how nice!
$_SESSION["errorMessages"] = ["You have logged out."];
// redirect to the loginForm.php page
header("Location: loginForm.php");
?>
In this code, we're doing the following things:
Calling session_start(), which seems odd, but we
have to do this or we can't access the session at all.
Clearing out all the session data by assigning an empty arra()
to $_SESSION. Note that calling session_destroy()
won't work here because we have session_start() at the top.
This also won't delete the session cookie, but that's fine because
we're actually going to keep using the $_SESSION data!
Add an element to the "errorMessages" session variable (we
just emptied it in the previous statement). Remember that on the
loginForm.php page, the "errorMessages" variable is an array, so
we add our one and only error message as an element (and it's not
really an error, just an informative message - you could have
done this differently, but this was the easiest for this very
simple example).
Redirect to the loginForm.php page: this will show our informative
"you have logged out" message, but it's also convenient: perhaps the
user might wish to log in as a different user...
Upload the new logout.php file and any other files you've changed.
Then delete the session cookie and start over by logging in with
valid credentials. After you see the welcome page, click the Log Out
link: you should see the "you have logged out" mesage above the
login form.
Notice that your session cookie is still there and has
the same ID: we haven't truely ended the session, just "started over".
To truely end the session, you need to delete the session cookie.
To do this, you can use setcookie("PHPSESSID", time() - 3600);
This will set the PHPSESSID's expiry time to one hour ago, resulting
in an expired session cookie. Feel free to experiment with it!
Final version of all files, in case you get stuck:
<?php
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// **NEVER store plain text password data EVER, this is a demo only**
// username => password
$pretendUserData = [
"foobar" => "password",
"wendi" => "tunafish",
"arti" => "iamboss"
];
// stores potential error messages
$errorMessages = [];
// get username and password from query string
$userName = filter_input(INPUT_POST, "username", FILTER_SANITIZE_SPECIAL_CHARS);
$password = $_POST["passwd"];
// if there's no username, that's an error
if (!$userName) {
$errorMessages[] = "User name is a required field. Please enter your username.";
}
// if there's no password, that's an error
if (!$password) {
$errorMessages[] = "Password is a required field. Please enter your password.";
}
// if we have no error messages so far, see if credentials are valid
if (!$errorMessages) {
// it's an error if this user doesn't exist in our pretend database
if (!userExists($userName)) {
$errorMessages[] = "Sorry, you are not in our database. Register for an account.";
// it's an error if the password for this user doesn't match what
// we've stored in our pretend database
} else if (!correctPassword($userName, $password)) {
$errorMessages[] = "Invalid username or password.";
}
}
// if there are any errors, go back to login page
if ($errorMessages) {
redirectToLogin($errorMessages);
// if there are no errors, move on to welcome page
} else {
redirectToWelcome($userName);
}
function redirectToLogin($errorMessages) {
// back to login page
$_SESSION["errorMessages"] = $errorMessages;
// TODO: UPDATE YOUR url
header("Location: https://jollymor.dev.fast.sheridanc.on.ca/examples/w2week11/loginForm.php", true, 302);
exit();
}
// it's tempting to turn redirectToLogin() and redirectToWelcome() into
// one re-usable function, which would be correct, but these two methods
// will be doing different things later so just leave it this way
function redirectToWelcome($userName) {
// go to welcome page
$_SESSION["username"] = $userName;
// TODO: UPDATE YOUR url
header("Location: https://jollymor.dev.fast.sheridanc.on.ca/examples/w2week11/welcome.php", true, 302);
exit();
}
// returns true if the specified $user exists in our
// pretend user data
function userExists($user) {
global $pretendUserData;
return array_key_exists($user, $pretendUserData);
}
// returns true if the specified $user's inputted $pass matches
// what is stored in our pretend user data
function correctPassword($user, $pass) {
global $pretendUserData;
return $pretendUserData[$user] === $pass;
}
?>
logout.php
<?php
// we need this so we can access the current session
// and its data
session_start();
// clear the session array
$_SESSION = array();
// add errorMessages with one element:
// why? we're going back to the loginForm page, so
// this will display in the DIV above the form, how nice!
$_SESSION["errorMessages"] = ["You have logged out."];
// redirect to the loginForm.php page
header("Location: loginForm.php");
?>
Exercises
1. Add a file sessionTest1.php to your project and paste in
the following code:
5. Now that you've completed each exercise, add the following statement to
sessionTest4b.php in the body of the document after the if/else blocks:
var_dump($_SESSION);
6. Create a simple page that lets you
manage your session variables. Provide a list of
check boxes for each session variable that currently
exists so you can check off which ones you want to
delete, and some input fields where you can add a new
session variable.
Tips:
Creating check boxes: use a foreach loop to iterate
through the $_SESSION array (foreach ($_SESSION as $key => $value)).
Inside the loop, create a labeled checkbox and use an
array as the name attribute value e.g. name='chk[]'
Using Delete All: use an if statement to check and
see if Delete All was checked - if it was, use one of the
PHP functions that removes all the session variables.
Otherwise, remove only the session variables that were
checked.