Sharing session data between Oscommerce and Zend Framework

Imagine you are running a web site using a content management system or a shopping cart such as Oscommerce. Now you need to add some custom code to enable a new functionality. May be you want to use a PHP framework like Zend. How to make sure that sessions are shared between Oscommerce and Zend Framework?

Let us look at how sessions work in PHP. Typically a web server creates a session by calling session_start() function. Once called, this function will generate a unique session id and set the Set-Cookie header field in the response.  The response may look like the following:

HTTP/1.0 200
Content-Length: 1512
Content-Type: text/html
Date: Tue, 06 Sep 20012 04:12:49 EDT
Expires: Tue, 06 Sep 2012 05:12:59 EDT
Set-Cookie: PHPSESSID=a9q8sfrsvfi4j10eic3lo2lss5; path=/admin/; domain=.mysite.com


This will cause the browser to create cookie named PHPSESSID  on the client. When you make another request to the web server, the browser will automatically send the cookie to the server. The request will be as follows:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-us, en;q=0.05
Cookie: PHPSESSID=a9q8sfrsvfi4j10eic3lo2lss5
Host: www.mysite.com

Note that session name (PHPSESSID), path (/admin/) and domain(.mysite.com) are configurable from the server. You will be able to change the configure.php file on the Oscommerce installation to change these values. 

By default, PHP creates a session file and stores it under /tmp. However, Oscommerce stores the session information in a MySQL table called sessions.

If we need to use Zend Framework code to share the same session, the domain name, and cookie path need to match. If the domain name for the cookie is set to www.mysite.com, then the Zend code has to reside somewhere in the path, www.mysite.com. However, if you choose the domain name ".mysite.com" the Zend code can reside even in a sub-domain such as api.mysite.com.

For this example, let us assume that the Zend Framework code resides under api.mysite.com so that we can share the session with the main site. We will use two classes, service and mapper, to interact with the sessions created by the main (Oscommerce) site.

The Service_OscSession class will provide the interfaces for initializing session while Mapper_OscSession will interact with the database.

The Service_OscSession uses the session_set_save_handler() to register all handlers that are defined in the Mapper_OscSession. 

class Service_OscSession {
  
  protected $_mapper;
  protected $_sessName = "usersId";
  protected $_sessDir = "/tmp";
  protected $_cookiePath = "";
  protected $_cookieDomain = ".mysite.com";
  
  public function __construct() {
    $this->_mapper = new Image_Model_Mapper_OscSession;
    
    session_set_save_handler(
            array($this->_mapper, 'open'),
            array($this->_mapper, 'close'),
            array($this->_mapper, 'read'),
            array($this->_mapper, 'write'),
            array($this->_mapper, 'destroy'),
            array($this->_mapper, 'gc'));
  }

  public function sessionStart() {
    $this->loadSession();
    return session_start();
  }

  private function loadSession() {
    $this->sessionName($this->_sessName);
    $this->savePath($this->_sessDir);
    session_set_cookie_params(0, $this->_cookiePath, $this->_cookieDomain);
  }
  
  public function isRegistered($variable) {
    return isset($_SESSION) && array_key_exists($variable, $_SESSION);
  }

  public function unregister($variable) {
    unset($_SESSION[$variable]);
  }

  public function sessionId($sessid = '') {
    if (!empty($sessid)) {
      return session_id($sessid);
    } else {
      return session_id();
    }
  }

  public function sessionName($name = '') {
    if (!empty($name)) {
      $this->_sessName= $name;
      return session_name($name);
      
    } else {
      return session_name();
    }
  }
  
  public function savePath($path = '') {
    if (!empty($path)) {
      return session_save_path($path);
    } else {
      return session_save_path();
    }
  }
}



Here is how the Mapper_OscSession class is implemented.

class Mapper_OscSession extends Zend_Db_Table {
  
  protected $_name = 'sessions';
  protected $_sess_life;
  
  public function __construct() {
    parent::__construct();
    
    if (!$this->_sess_life = get_cfg_var('session.gc_maxlifetime')) {
      $this->_sess_life = 1440;
    }
    
    register_shutdown_function('session_write_close');
  }
  
  public function open($save_path, $session_name) {
    return true;
  }
  
  public function close() {
      return true;
  }
  
  public function read($did = null) {
    
    $select = $this->select()
            ->from($this->_name, array('value'))
            ->where('sesskey=?', $did)
            ->where('expiry>?', time());

    $row = $this->getAdapter()->fetchRow($select);

    if ($row == false) {
      return null;
    }

    return $row['value'];
  }
  
  public function write($key, $val) {
    
    $expiry = time() + $this->_sess_life;
    
    $select = $this->select()
            ->from($this, array('count(*) as total'))
            ->where('sesskey=?', $key);
    
    $rows = $this->fetchAll($select);
    
    if ($rows[0]->total > 0) {
      return $this->update(array('expiry'=> $expiry, 'value' => $val), 'sesskey=' . $key);
      
    } else {
      return $this->insert(array('expiry'=> $expiry, 'value' => $val, 'sesskey' => $key));
    }
  }
  
  function destroy($key) {
    $this->delete('sesskey=?', $key);
  }

  function gc($maxlifetime) {
     return true;
  }
}

Ready for the final step? Just add following few lines of code and you have full access to OsCommerce session data.

$svc = new Service_OscSession();
$svc->sessionName("mysession");
$svc->sessionStart();

$userId = $svc->isRegistered("customers_id");

if ($userId) {
  print "user is logged in";
} else {
  print "please login";
}

Comments