Zebra_Session, a wrapper for PHP’s default session handling functions, using MySQL for storage

Get the latest updates on this PHP library via RSS

Session support in PHP consists of a way to preserve information (variables) on subsequent accesses to a website’s pages. Unlike cookies, variables are not stored on the user’s computer. Instead, only a session identifier is stored in a cookie on the visitor’s computer, which is matched up with the actual session data kept on the server, and made available to us through the $_SESSION super-global. Session data is retrieved as soon as we open a session, usually at the beginning of each page.

By default, session data is stored on the server in flat files, separate for each session. The problem with this scenario is that performance degrades proportionally with the number of session files existing in the session directory (depending on the server’s operating system’s ability to handle directories with numerous files). Another issue is that session files are usually stored in a location that is world readable posing a security concern on shared hosting.

This is where Zebra_Session comes in handy – a PHP library that acts as a drop-in replacement for PHP’s default session handler, but instead of storing session data in flat files it stores them in a MySQL database, providing better security and better performance.

Zebra_Session is also a solution for applications that are scaled across multiple web servers (using a load balancer or a round-robin DNS) where the user’s session data needs to be available. Storing sessions in a database makes them available to all of the servers!

Supports “flashdata” – session variables which will only be available for the next server request, and which will be automatically deleted afterwards. Typically used for informational or status messages (for example: “data has been successfully updated”).

This class is was inspired by John Herren’s code from the Trick out your session handler article and Chris Shiflett’s code from his book Essential PHP Security, chapter 8, Shared Hosting, Pg. 78-80.

Zebra_Session‘s code is heavily commented and generates no warnings/errors/notices when PHP’s error reporting level is set to E_ALL.

Starting with version 2.0, Zebra_Session implements row locks, ensuring that data is correctly handled in a scenario with multiple concurrent AJAX requests.

Citing from Race Conditions with Ajax and PHP Sessions, a great article by Andy Bakun:

When locking is not used, multiple requests (represented in these diagrams as processes P1, P2 and P3) access the session data without any consideration for the other processes and the state of the session data. The running time of the requests are indicated by the height of each process’s colored area (the actual run times are unimportant, only the relative start times and durations).

Session access without locking

In the example above, no matter how P2 and P3 change the session data, the only changes that will be reflected in the session are those that P1 made because they were written last. When locking is used, the process can start up, request a lock on the session data before it reads it, and then get a consistent read of the session once it acquires exclusive access to it. In the following diagram, all reads occur after writes:

Session access with locking

The process execution is interleaved, but access to the session data is serialized. The process is waiting for the lock to be released during the period between when the process requests the session lock and when the session is read. This means that your session data will remain consistent, but it also means that while processes P2 and P3 are waiting for their turn to acquire the lock, nothing is happening. This may not be that important if all of the requests change or write to the session data, but if P2 just needs to read the session data (perhaps to get a login identifier), it is being held up for no reason.

So, in the end, this is not the best solution but still is better than nothing. The best solution is probably a per-variable locking. You can read a very detailed article about all this in Andy Bakun‘s article Race Conditions with Ajax and PHP Sessions.

Thanks to Michael Kliewe who brought this to my attention!

Top

Features review

  • acts as a wrapper for PHP’s default session handling functions, but instead of storing session data in flat files it stores them in a MySQL database, providing better security and better performance
  • it is a drop-in and seemingless replacement for PHP’s default session handler: PHP sessions will be used in the same way as prior to using the library; you don’t need to change any existing code!
  • implements row locks, ensuring that data is correctly handled in scenarios with multiple concurrent AJAX requests
  • because session data is stored in a database, the library represents a solution for applications that are scaled across multiple web servers (using a load balancer or a round-robin DNS)
  • has comprehensive documentation
  • the code is heavily commented and generates no warnings/errors/notices when PHP’s error reporting level is set to E_ALL
Top

Requirements

PHP 5.1.0+, MySQL 4.1.22+

Top

Installation

Download the latest version, unpack it, and put it in a place accessible to your scripts. After unpacking, you will notice a directory called “install” containing a file named “session_data.sql”. This file contains the SQL code that will create a table that is used by the class to store session data. Import or execute the SQL code using your preferred MySQL manager (like phpMyAdmin or the fantastic Adminer) into a database of your choice.

Top

How to use

Note that this class assumes that there is an active connection to a MySQL database and it does not attempt to create one! If you really need the class to make a database connection, put the code in the “open” method of the class.

<?php

    // first, connect to a database containing the sessions table

    // include the Zebra_Session class
    include 'path/to/Zebra_Session.php';

    // instantiate the class
    // this also calls session_start()
    $session = new Zebra_Session;

    // from now on, use sessions as you would normally
    // this is why it is called a "drop-in replacement" :)
    $_SESSION['foo'] = 'bar';

    // data is in the database!

?>

Top

Download

version 2.1.0
If you find this library to be useful to you, you can support the author by donating a small amount via PayPal:

Zebra_Session is distributed under the LGPL.

In plain English, this means that you have the right to view and to modify the source code of this software, but if you modify and distribute it, you are required to license your copy under a LGPL-compatible license, and to make the entire source code of your derivation available to anybody you distribute the software to.

You also have the right to use this software together with software that has different licensing terms (including, but not limited to, commercial and closed-source software), and distribute the combined software, as long as state that your software contains portions licensed under the LGPL license, and provide information about where the LGPL licensed software can be downloaded.

If you distribute copies of this software you may not change the copyright or license of this software.


You may also like:

Top

Documentation

Documentation Become a ninja.
Read the comprehensive documentation.

Top

Changelog

Click on a version to expand/collapse information.

version 2.1.1 (September 25, 2014)
  • this version makes use of a feature introduced in PHP 5.1.0 for the “regenerate_id” method; thanks to Fernando Sávio; this also means that now the library requires PHP 5.1.0+ to work
version 2.1.0 (August 02, 2013)
  • dropped support for PHP 4; minimum required version is now PHP 5;
  • dropped support for PHP’s mysql extension, which is officially deprecated as of PHP v5.5.0 and will be removed in the future; the extension was originally introduced in PHP v2.0 for MySQL v3.23, and no new features have been added since 2006; the library now relies on PHP’s mysqli extension;
  • because of the above, the order of the arguments passed to the constructor have changed and now the “link” argument comes first, as with the mysqli extension this now needs to be explicitly given;
  • added support for “flashdata” – session variable which will only be available for the next server request, and which will be automatically deleted afterwards. Typically used for informational or status messages (for example: “data has been successfully updated”); see the newly added set_flashdata method; thanks Andrei Bodeanschi for suggesting!;
  • the project is now available on GitHub and also as a package for Composer;
version 2.0.4 (January 19, 2013)
  • previously, session were always tied to the user agent used when the session was first opened; while this is still true, now this behavior can be disabled by setting the constructor‘s new “lock_to_user_agent” argument to FALSE; why? because in certain scenarios involving Internet Explorer, the browser will randomly change the user agent string from one page to the next by automaticaly switching into compatilibity mode; thanks to Andrei Bodeanschi;
  • also, the constructor has another new “lock_to_ip” argument with which a session can be restricted to the same IP as when the session was first opened – use this with caution as many ISPs provide dynamic IP addresses which change over time, not to mention users who come through proxies; this is mostly useful if you know your users come from static IPs;
  • for better protection against session hijacking, the first argument of the constructor is now mandatory;
  • altered the table structure;
  • because the table’s structure has changed as well as the order of the arguments in the constructor, you’ll have to change a few things when switching to this version from a previous one;
  • some documentation refinements;
version 2.0.3 (July 13, 2012)
  • fixed a bug where sessions’ life time was twice longer than expected; thanks to Andrei Bodeanschi;
  • details on how to preserve session data across sub domains was added to the documentation;
  • the messages related database connection errors are now more meaningful;
version 2.0.2 (October 24, 2011)
  • fixed a bug with the get_active_sessions() method which was not working at all;
  • fixed a bug where the script was not using the provided MySQL link identifier (if available);
version 2.0.1 (July 03, 2011)
  • the constructor method now accepts an optional link argument which must be a MySQL link identifier. By default, the library made use of the last link opened by mysql_connect(). On some environments (particularly on a shared hosting) the “last link opened by mysql_connect” was not available at the time of the instantiation of the Zebra_Session library. For these cases, supplying the MySQL link identifier to the constructor method will fix things. Thanks to Mark for reporting.
  • some documentation refinements
version 2.0 (April 18, 2011)
  • the class now implements session locking; session locking is a way to ensure that data is correctly handled in a scenario with multiple concurrent AJAX requests; thanks to Michael Kliewe for suggesting this and to Andy Bakun for this excellent article on the subject Race Conditions with Ajax and PHP Sessions.
version 1.0.8 (December 27, 2010)
  • fixed a small bug in the destroy method; thanks to Tagir Valeev for reporting;
  • the script would trigger a PHP notice if the HTTP_USER_AGENT value was not available in the $_SERVER super-global;
  • added a new method “get_settings” that returns the default session-related settings for the environment where the class is used
version 1.0.7 (October 29, 2008)
  • the class will now trigger a fatal error if a database connection is not available;
  • the class will now report if MySQL table is not available;
version 1.0.6 (October 01, 2007)
  • the constructor of the class now accepts a new argument: tableName – with this, the MySQL table used by the class can be changed;
version 1.0.5 (September 15, 2007)
  • ‘LIMIT 1′ added to the read method improving the performance of the script; thanks to A. Leeming for suggesting this;
version 1.0.4 (August 23, 2007)
  • rewritten the write method which previously had to run two queries each time; it now only runs a single one, using ON DUPLICATE KEY UPDATE; thanks to Inchul Koo for providing this information;
  • the type of the http_user_agent column in the MySQL table has been changed from TEXT to VARCHAR(32) as now it is an MD5 hash; this should increase the performance of the script; thanks to Inchul Koo for suggesting this;
  • the constructor of the class now accepts a new argument: securityCode, in order to try to prevent HTTP_USER_AGENT spoofing; read the documentation for more information; thanks to Inchul Koo for suggesting this;
version 1.0.3 (December 13, 2006)
  • the get_users_online method is now more accurate as it now runs the garbage collector before getting the number of online users; thanks to Gilles for the tip;
  • the structure of the MySQL table used by the class was tweaked in so that the http_user_agent column has been changed from VARCHAR(255) to TEXT and the session_data column has been changed from TEXT to BLOB; thanks to Gilles for the tip
version 1.0.2 (November 22, 2006)
  • the class was trying to store the session data without mysql_real_escape_string-ing it and therefore, whenever the data to be saved contained quotes, nothing was saved; thanks to Ed Kelly for reporting this;
version 1.0.1 (September 11, 2006)
  • the structure of the MySQL table used by the class was tweaked and now the session_id column is now a primary key and its type has changed from VARCHAR(255) to VARCHAR(32) as it now is an MD5 hash; previously a whole table scan was done every time a session was loaded; thanks to Harry for suggesting this;
  • added a new stop method which deletes a session along with the stored variables from the database; this was introduced because many people were using the private destroy method which is only for internal purposes;
  • the default settings for session.gc_maxlifetime, session.gc_probability and session.gc_divisor can now be overridden through the constructor;
  • on popular request, an example file was added;
version 1.0 (August 01, 2006)
  • initial release

Top

89 responses to “Zebra_Session, a wrapper for PHP’s default session handling functions, using MySQL for storage”

Follow the comments via RSS
  • Ngoc Phuong, 2013-02-17, 04:10

    Hello Stefan Gabos, thank for your sharing, i really like your class.
    I have a small probem using your session handler class,
    i have mysql_close function on destruct of database class and it prevent Zebra_Session from connecting to database. Is it necessary to remove mysql_close function on destruct? it wil make the web page keep connection with database server and server will harder, thank you!

    Reply
    • Stefan Gabos, 2013-02-17, 09:36

      You can remove “mysql_close” from the constructor as mysql connections “are automatically closed at the end of the script’s execution” (see http://php.net/manual/en/function.mysql-close.php).

    • Ngoc Phuong, 2013-02-17, 12:30

      thank you very much!

  • Neil S Hamilton, 2013-03-06, 15:47

    Hi there,

    Firstly congratulations on a really useful PHP class. I was going to write this myself when we moved to having 2 webservers running of one MySQL server, but then I came across this, which does a better job than anything I could have written.

    However, I think there may be an issue with the fact that this class adds an extra layer of security above PHP standard session management, by noting the value of $_SERVER['HTTP_USER_AGENT']. The problem with this approach, as I’ve seen over the past couple of weeks, is that when, say, Chrome updates its user agent string (as happens frequently for Chrome and probably Firefox) then because the user agent no longer matches, the session can’t be found. Except because the cookie value of PHP’s session ID is still the same, the session doesn’t recreate, it just has blank data and won’t save anything.

    My solution to this was to add an option to disable the HTTP_USER_AGENT check – strictly it shouldn’t be necessary as PHP sessions are generally considered secure enough for most things.

    There may be a better way round this, but that’s what I’ve done, so we don’t alienate all our Chrome users :-)

    Reply
    • Stefan Gabos, 2013-03-06, 15:54

      The same happens for Internet Explorer users when IE switches on and off of compatibility mode, by itslef;
      If you are using the latest version, then all you have to do is to set the “lock_to_user_agent” argument of the constructor to FALSE; see the documentation.
      Thanks!

  • Lindsay, 2013-04-02, 02:08

    Great code. I had a session library that worked until they upgraded the server to a cluster and then it fell over. But I popped in your code and bingo! :-)

    One question: I saw your code for handling sub-domains but is there a simple approach to passing sessions across two totally different domains?

    Reply
  • damian, 2013-06-03, 10:42

    you have a rest of code for forhiben acces to the webpage? i like to know your way for autentification, or example of aplication in autorization system. thanks

    Reply
    • Stefan Gabos, 2013-06-04, 04:49

      nope. this library is for session management, which, incidentally, can be used to build an authentication system. google for “php authentication library” and you’ll probably find what you are looking for

  • Marciano Dias, 2013-07-30, 11:45

    When I access the page shows:
    Zebra_Session: No database selected

    detail:
    – I’m trying to use the Zebra_Database concetar up.
    – If I use:
    mysql_connect (SERVER, USER, PASS, DB);
    mysql_select_db (DB);
    … WORKS. HOWEVER, I WOULD LIKE TO USE ZEBRA_DATABASE.

    Reply
    • Stefan Gabos, 2013-08-02, 07:09

      unfortunately this is isn’t going to work as Zebra_Session uses the old mysql extension while Zebra_Database (the new version) uses the mysqli extension. I will release an updated version of Zebra_Session but can’t give you a time frame. Until then, you could use an older version of Zebra_Database

    • Stefan Gabos, 2013-08-03, 12:31

      I’ve released an updated version of Zebra_Session which can be used together with the latest version of Zebra_Database – it turns out I’ve already made the switch just forgot about it…

    • Marciano Dias, 2013-11-08, 01:07

      After upgrading, I got the following error:
      Zebra_Session: Unknown column ‘hash’ in ‘where clause’

    • Stefan Gabos, 2013-11-08, 09:38

      that’s because in 2.0.4 table structure has changed; now the column called previously “http_user_agent” is now called “hash”

    • Marciano Dias, 2013-11-09, 13:37

      excuse me, please. was my lack of attention. had already solved the problem. anyway, thanks.

  • Sandeep, 2013-08-27, 18:26

    Good Library, i liked it as i could use it with my existing website.

    So, I have tried to use Zebra Session today, and it carries session up to 2 pages after that i see it does not have session data in DB and i get redirected back to login page.

    Reply
  • Sandeep, 2013-08-28, 07:32

    Hi,

    I had PHP 5.3 on server and was not able to upgrade to PHP 5.5. so i update your great library for PHP 5.5 my_sql connections and it works great. Thanks a lot to save me.

    I think you should put both library on server for lesser PHP 5.5 and for PHP 5.5. i can share the library if you need to put it here so it can help more developers.

    Thanks alot once again.

    Reply
  • coolo, 2013-09-11, 02:15

    Thanks for the code. Looks like I was able to make this work with mysqli just now. I had to explicitly send $link when instantiating the class, change all the _mysql wrapper functions to the appropriate syntax for mysqli, and find the 2 spots (in the read function) validating for resources and instead validate for objects. Took a little while (I am a novice at this), but I figured it out.

    Reply
    • coolo, 2013-09-11, 02:23

      Aw man, now I see that there is a new version that already has mysqli support built in. *slaps head*

  • confused, 2014-03-06, 12:54

    I am trying to use this code but when the page is reloaded the session variables are no where to be seen.

    There seems to be an issue with the read() function. Using mysqli here.

    Reply
  • Heart, 2014-07-06, 18:34

    Hello! I like your class. I wanted to ask that can I use it in my application which is intended to be sold?

    Reply
    • Stefan Gabos, 2014-07-13, 09:30

      you can use it.

  • Wane, 2014-09-14, 14:52

    I am getting a message: zebra_session: could not obtain session lock!
    Did I configure something wrong?

    Reply

Leave a Reply

Your email address will not be published
You can use <strong>, <em>, <a>, <img>, <code>
Characters are not case-sensitive