Updated: 23rd March 2012

Maintaining State in a PHP Web Application.

Web applications often need to identify users and track each user's state as they interact with the application. Web applications are built on top of the HTTP protocol which is essentially a stateless protocol. This presents a problem.

Ways to Maintain State

To demonstrate the various methods used to maintain state in a PHP web application we will use an example.

You are building a web application for a college which involves a multi page form wizard. The first page collects some personal details such as email, password, name and address. The second page asks you to select a course to enrol into. The third page presents the information back to the user to confirm. The fourth page thanks the user for enrolling into the course.

Query String

A user's state can be transmitted to the server through the URL in the query string. Following the example above, after the user submits the first form you would collect the users personal details and then when generating your HTML for the second page would append the user details to the query string of the form submission URL. This way when the user submits the second page the personal data will be available in the query string along with the second pages form data. You can continue this process throughout the wizard.

Hidden Form Fields

A user's state can be transmitted to the server inside hidden fields in a form. Following the example above, after the user submits the first form you would collect the user's personal details and then when generating your HTML for the second page would place the user details into hidden form fields. This way when the user submits the second page the personal data will be available alongside the data from the second form. You can continue this process throughout the wizard.

Cookies

A user's state can be stored in web browser cookies and transmitted to the server on each request. Following the example above, after the user submits the first form you would collect the user's personal details and then before generating your HTML for the second page you would set the HTTP header Set-Cookie containing the user data. When the user submits the second form the cookie data will be sent to the server along with the form submission. You can continue this process throughout the wizard.

PHP Sessions

A user's state can be stored in the PHP $_SESSION. Each PHP session has a unique session id. This session id is passed from page to page using either a cookie or in the query string. In either case it is unique for a particular user and so can be used to track a users state. The actual data is stored on the server either in the file system or a database. The session will be destroyed when the user closes their browser.1 Following the example after the user submits the first form you would collect the user's personal details and then start a session (which creates the unique session id and sends it in a cookie) and store the data in $_SESSION. When the user submits the second form the cookie containing the session id will be sent along with the request and PHP will then use it to lookup the session data (on disk or in database) and generate $_SESSION with the personal data included. You can continue this process throughout the wizard.

Choose the Right Method

For the example we have been using thus far the best method would be using hidden form fields. Using cookies or the PHP session would actually be a very bad idea for a form wizard as you will soon see why. There is no exact science in choosing the method to track the user session, it all depends on the circumstances. Here are some common situations and the best method for each.

Keeping Track of Authentication State

You want to keep track of the privilege level of the users accessing your web application. Typically you will have multiple privilege levels starting with anonymous and working up. So say you define 3 levels: anonymous, editor and admin. In order for the user to prove who they are you get them to enter a username and password. If they login correctly you will want to work out which privilege level they are at and keep track of this over many HTTP request/response cycles. This is the perfect place to use the PHP session. It would be a huge security risk to pass this state information around using the query string or in hidden form fields (in the case of hidden form fields also impractical). If you used the query string, in order to increase their privilege level all a malicious user would need to so is modify the URL. Hidden form fields are also easily tampered with. Cookies which directly stored the privilege level would also be easily modified. PHP sessions on the other hand are identified with a hard to guess unique id. The only thing stored in this Cookie is the session id. Unless you can guess the id and hijack someones session2 you won't be able to change your privilege level. You could get the user to manually login on every request but this isn't tracking state and highlights why you need to track the user state. It would be a huge pain for the user to be constantly typing in their username and password. You might be thinking you could just put the username and password into a cookie. However this is unnecessarily exposing their login credentials. Note that even with the proper use of PHP sessions to build a secure login system requires extra precautions beyond the scope of this article.

Tracking a User Through a Form Wizard

As I mentioned the best way to track this kind of user state would be with hidden form fields. The first thing you might think in this situation is to use the PHP session. The problem here is when the user opens multiple tabs in their browser. Following our original example, if they opened two tabs to join two different courses simultaneously. In both cases the same cookie will be shared. So you have a race condition of sorts. Data entered in one tab can be overwritten by data from another tab. You could use the query string instead of this method however some browsers have a limit on the length of the URL and the form data could get quite big.

Pagination and Search Results

This situation pops up anytime you need to split the results of a database query over multiple pages. You need to keep track of what page the user is currently on as they go jump page to page through the results. Using the session or a cookie is a bad idea because of the reasons stated in the last example. You could use hidden form fields and use forms to POST between pages, however this would stop people bookmarking a particular page of search results. Plus these types of retrievals should be done using the GET method. So your best choice is to pass the search parameters and the current page along in the query string with every page navigation link.

Remember a Users Preference Between Sessions

Say you want to allow a user to save their search preferences so each time they visit your site the home page presents them with news items of interest to them. You could get them to login each time they come back to the site and grab the preferences from the database. However for data like search preferences which isn't really very sensitive you are better off using a persistent cookie. This way the user won't have to login each time they come back to the site.

Footnotes

  1. Technically the session data will remain on the server for some time until cleaned up by PHP but the browser will lose track of the id when it's closed.
  2. You could hijack the session in other ways, but there are ways to stop these kinds of attacks. In any case this is more secure than the other methods.