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