Tracking the user’s browsing history with PHP

Tags:

There are various reasons for tracking what an user does:

  • Keeping track of the refering page for forms, so they can be sent back to where they came from
  • Tracking usage behavior for statistics
  • etc.

Let’s look at how we could implement a simple user tracker in PHP with Zend Framework. I’m going to write a bit of the theory too, so this might help you even if you’re not using ZF.

The basic idea

As we know, the browser keeps a history of visited pages. However, we can’t access this at all, with the exception of some older version of IE (I think), so we must think of some other way to do it.

The browser usually lets the server see the exact previous page where it came from: the referer URL.
There’s just one problem: it isn’t always populated. Some firewalls can optionally block it, some browsers have an option to turn it off etc., so it can’t be used very reliably.

This is why we need a custom method for tracking urls. Of course, this will only work on our site, but that doesn’t matter – we don’t want to literally spy on our users, now do we?

Since we can see each request the user does to our site, we can employ a simple strategy: Every time the user opens a page, store the opened page’s URL in an array in the session. This way we can collect the browsing history on our page.

The implementation

We could implement this in Zend Framework as a front controller plugin and it would get automatically executed on every request, but it has a limitation. Since it would probaby be nice to be able to access the history, for example to redirect the user back to a page, we can’t go with the front controller plugin.

The best option would probably be a action helper. Just like front controller plugins, they can get automatically executed on every request, but they can also be accessed from the controller actions, making it possible to use the history data easily.

In case you haven’t heard about action helpers, check the action helpers chapter of Zend Framework manual. It will help you understand what the code we’ll be writing soon does.

The code

You can download the code from the Svn. Let’s look at it a bit:

The class should be rather obvious for the most part. Most of the things happen in _initSession and preDispatch, which handle the tracking.

private function _initSession()
{
    $this->_namespace = new Zend_Session_Namespace('CU_Controller_Action_Helper_History');
 
    if(!is_array($this->_namespace->history))
    {
        $this->_namespace->history = array();
 
        if(!empty($_SERVER['HTTP_REFERER']))
            array_unshift($this->_namespace->history, $_SERVER['HTTP_REFERER']);
    }
    else	
        array_splice($this->_namespace->history, $this->_trackAmount);
}

Despite being the biggest method of the class, _initSession is rather simple. It opens a session namespace and sees if there was previous data for the history or not. In case there was nothing in, it also checks the HTTP_REFERER header; this allows us to see at least the refering site where they came from. If there was some data in, it uses array_splice to cut the length of the history to the max limit.

preDispatch simply uses the Url helper to get the current URL and uses array_unshift to make it the 0th index in the array. Rest of the class should be quite obvious.

As can be seen, the history logic works quite simply. Each URL is kept in an array in the session in a namespace so there will be no collisions. The current page is the 0th, the previous is 1st, the one before that 2nd and so on. The default amount of URLs to track is two because that should be enough for the typical scenario of form redirection: You go to a page which links to the form, which you submit, making the initial page the 2nd in history.

Using the class

Using the class is very simple. First, we must add the helper to the HelperBroker, for example in our bootstrap:

Zend_Controller_Action_HelperBroker::addHelper(new CU_Controller_Action_Helper_History());

This is so that the helper gets initialized and will be notified on each request. If we used addPath, the helper would not get initialized before it’s used in a controller, effectively missing preDispatch.

Now, we can use this in our controller:

public function someAction()
{
    $this->_helper->history->goBack(2); //jump back two pages in history
}

We can also use getPreviousUrl or getArray to get the visited URLs without redirecting.

Conclusion

This is a pretty useful feature in my opinion. I often need this kind of things with my forms and so far I’ve actually rewritten the same redirection code again and again.

This could also be expanded from here to track more specific actions, perhaps even how long the user spent between requests. That way it could be used for creating statistics of user behavior on the site, such as navigation paths and other things.

More on PHP:
Three PHP mistakes that will cause you debugging nightmares
Generic collections in PHP
Automating creation and caching of image thumbnails