Here’s my second Doctrine 1.2 driver for Kohana 3, this time for the Session class. I wrote this because I don’t particularly like the Kohana Database or ORM classes (that or I just don’t want to take the time to learn them!) and it’s released under a creative commons v3 licence So here it is. Enjoy!
Kohana 3 Doctrine Session Driver 1.02 See here for details
UPDATE 22 Oct 2010: Bugfix release – should actually work now 🙂
UPDATE 03 Jan 2011: Fixed rare bug in DB cleaning function.
UPDATE 21 Feb 2011: New module at a new home! See here for details
<?php defined('SYSPATH') or die('No direct script access.'); /** * Database-based session class. * * Sample schema: * * CREATE TABLE `sessions` ( * `session_id` VARCHAR( 24 ) NOT NULL, * `last_active` INT UNSIGNED NOT NULL, * `contents` TEXT NOT NULL, * PRIMARY KEY ( `session_id` ), * INDEX ( `last_active` ) * ) ENGINE = MYISAM ; * * @package Kohana/Database * @category Session * @author Kohana Team * @copyright (c) 2008-2009 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Session_Database extends Session { // Database instance protected $_db; // Database table name protected $_table = 'sessions'; // Database column names protected $_columns = array( 'session_id' => 'session_id', 'last_active' => 'last_active', 'contents' => 'contents' ); // Garbage collection requests protected $_gc = 500; // The current session id protected $_session_id; // The old session id protected $_update_id; public function __construct(array $config = NULL, $id = NULL) { if ( ! isset($config['group'])) { // Use the default group $config['group'] = 'default'; } // Load the database $this->_db = Database::instance($config['group']); if (isset($config['table'])) { // Set the table name $this->_table = (string) $config['table']; } if (isset($config['gc'])) { // Set the gc chance $this->_gc = (int) $config['gc']; } if (isset($config['columns'])) { // Overload column names $this->_columns = $config['columns']; } parent::__construct($config, $id); if (mt_rand(0, $this->_gc) === $this->_gc) { // Run garbage collection // This will average out to run once every X requests $this->_gc(); } } public function id() { return $this->_session_id; } protected function _read($id = NULL) { if ($id OR $id = Cookie::get($this->_name)) { $result = DB::select(array($this->_columns['contents'], 'contents')) ->from($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->limit(1) ->param(':id', $id) ->execute($this->_db); if ($result->count()) { // Set the current session id $this->_session_id = $this->_update_id = $id; // Return the contents return $result->get('contents'); } } // Create a new session id $this->_regenerate(); return NULL; } protected function _regenerate() { // Create the query to find an ID $query = DB::select($this->_columns['session_id']) ->from($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->limit(1) ->bind(':id', $id); do { // Create a new session id $id = str_replace('.', '-', uniqid(NULL, TRUE)); // Get the the id from the database $result = $query->execute($this->_db); } while ($result->count()); return $this->_session_id = $id; } protected function _write() { if ($this->_update_id === NULL) { // Insert a new row $query = DB::insert($this->_table, $this->_columns) ->values(array(':new_id', ':active', ':contents')); } else { // Update the row $query = DB::update($this->_table) ->value($this->_columns['last_active'], ':active') ->value($this->_columns['contents'], ':contents') ->where($this->_columns['session_id'], '=', ':old_id'); if ($this->_update_id !== $this->_session_id) { // Also update the session id $query->value($this->_columns['session_id'], ':new_id'); } } $query ->param(':new_id', $this->_session_id) ->param(':old_id', $this->_update_id) ->param(':active', $this->_data['last_active']) ->param(':contents', $this->__toString()); // Execute the query $query->execute($this->_db); // The update and the session id are now the same $this->_update_id = $this->_session_id; // Update the cookie with the new session id Cookie::set($this->_name, $this->_session_id, $this->_lifetime); return TRUE; } protected function _destroy() { if ($this->_update_id === NULL) { // Session has not been created yet return TRUE; } // Delete the current session $query = DB::delete($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->param(':id', $this->_update_id); try { // Execute the query $query->execute($this->_db); // Delete the cookie Cookie::delete($this->_name); } catch (Exception $e) { // An error occurred, the session has not been deleted return FALSE; } return TRUE; } protected function _gc() { if ($this->_lifetime) { // Expire sessions when their lifetime is up $expires = $this->_lifetime; } else { // Expire sessions after one month $expires = Date::MONTH; } // Delete all sessions that have expired DB::delete($this->_table) ->where($this->_columns['last_active'], '<', ':time') ->param(':time', time() - $expires) ->execute($this->_db); } } // End Session_Database |
Put it in modules/session_doctrine/classes/session/doctrine.php remembering to enable the module in your bootstrap file.
To save sessions in the database in Kohana 3, the following ‘sessions’ table is required (MySQL implementation):
CREATE TABLE sessions ( session_id VARCHAR(24) NOT NULL, last_active INT(10) UNSIGNED NOT NULL, contents text NOT NULL, PRIMARY KEY (session_id), KEY last_active (last_active) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; |
Because we’re using Doctrine, we’ll of course need a model. Seeing as Session is already taken I’ve decided to name mine Sessions (horrible I know – if you have a better suggestion feel free to comment! ). Below is the YAML schema for it:
Sessions: tableName: sessions columns: session_id: type: string(24) primary: true last_active: integer(4) contents: string indexes: last_active_index: fields: [last_active] |
and here’s the model file itself (autogenerated from the YAML):
class Sessions extends Doctrine_Record { public function setTableDefinition() { $this->setTableName('sessions'); $this->hasColumn('session_id', 'string', 24, array( 'type' => 'string', 'primary' => true, 'length' => '24', )); $this->hasColumn('last_active', 'integer', 4, array( 'type' => 'integer', 'length' => '4', )); $this->hasColumn('contents', 'string', null, array( 'type' => 'string', )); $this->index('last_active_index', array( 'fields' => array( 0 => 'last_active', ), )); } public function setUp() { parent::setUp(); } } |
I’ve attached the related files below for your downloading pleasure.
Kohana 3 Doctrine Session Driver 1.02 See here for more details
UPDATE 22 Oct 2010: Bugfix release – should actually work now 🙂
UPDATE 03 Jan 2011: Fixed rare bug in DB cleaning function.
UPDATE 21 Feb 2011: New module at a new home! See here for details