<?php
// +----------------------------------------------------------------------+
// | PHP mysql extension based shared memory class for PHP5. |
// | Copyright (C) 2005 Craig Manley |
// +----------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU Lesser General Public License as |
// | published by the Free Software Foundation; either version 2.1 of the |
// | License, or (at your option) any later version. |
// | |
// | This library is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | Lesser General Public License for more details. |
// | |
// | You should have received a copy of the GNU Lesser General Public |
// | License along with this library; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// | USA |
// | |
// | LGPL license URL: http://opensource.org/licenses/lgpl-license.php |
// +----------------------------------------------------------------------+
// | Author: Craig Manley |
// +----------------------------------------------------------------------+
//
// $Id: MySQL.php,v 1.2 2005/01/09 22:39:54 cmanley Exp $
//
/**
* @author Craig Manley
* @copyright Copyright © 2005, Craig Manley. All rights reserved.
* @package IPC_SharedMem
* @version $Revision: 1.2 $
*/
/**
* @ignore Require interface class.
*/
require_once(dirname(__FILE__) . '/../SharedMem.php');
/**
* PHP mysql extension based shared memory class.
* Makes use of the mysql_* functions and a mysql database as a form of shared memory.
* WARNING: Old versions of Mysql (I think <= v3.53) occasionally hang when using named locks.
*
* @package IPC_SharedMem
* @see http://www.php.net/manual/en/ref.mysql.php
*/
class IPC_SharedMem_MySQL implements IPC_ISharedMem {
// Private members
private $key = null;
private $dbh = null;
private $lock_name = null;
private $option_table = 'SharedMemory';
private $option_timeout = 10;
private $option_remove = false;
private $locked = false; // only set if in transaction mode.
/**
* Constructor.
*
* The table being shared must contain 2 fields:
* <ul>
* <li>id - the primary key.</li>
* <li>data - the blob field which contains the shared data.</li>
* </ul>
*
* The following options can be set in the 2nd parameter:
* <ul>
* <li>create - create the table if it does not exist, default true.</li>
* <li>remove - boolean, delete the shared record when this object is destroyed, default false.</li>
* <li>table - the table name to use as shared memory. Default is 'SharedMemory'.</li>
* <li>timeout - the maximum time in seconds to wait for a record lock, default 10.</li>
* </ul>
*
* @param resource $dbh a mysql resource link connected to a database.
* @param string $key the primary key of the record to share (case sensitive, max 32 chars).
* @param array $options associative array of options.
* @return object
*/
public function __construct($dbh, $key, $options = null) {
$this->dbh = $dbh;
$this->key = $key;
// Set options
if (isset($options)) {
if (isset($options['remove'])) {
$this->option_remove = $options['remove'];
}
if (isset($options['table'])) {
$this->option_table = $options['table'];
}
if (isset($options['timeout']) && preg_match('^\d{1,10}$/', $options['timeout']) && ($options['timeout'] > 0)) {
$this->option_table = $options['timeout'];
}
if (isset($options['create']) && $options['create']) {
// Create the table if it does not exist
$sql = 'CREATE TABLE IF NOT EXISTS ' . $this->option_table . ' (id CHAR(32) BINARY NOT NULL PRIMARY KEY, data MEDIUMBLOB)';
$sth = mysql_query($sql,$dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception('Failed to create shared memory table. Error: ' . mysql_errno($dbh) . ": " . mysql_error($dbh));
}
}
}
$this->lock_name = $this->option_table . "_$key";
}
/**
* Destructor. Removes the shared memory record if the 'remove' option is true.
*/
public function __destruct() {
if ($this->option_remove) {
$dbh = $this->dbh;
$sql = 'DELETE FROM ' . $this->option_table . ' WHERE id="' . mysql_real_escape_string($this->key, $dbh) . '"';
$sth = mysql_query($sql,$dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception(mysql_errno($dbh) . ": " . mysql_error($dbh));
}
}
$this->transaction_finish();
}
/**
* Determines if this object is in transaction mode (i.e. it has a record lock).
*
* @return boolean
*/
public function in_transaction() {
return isset($this->locked) && $this->locked;
}
/**
* Locks the record.
*
* @return boolean
*/
public function transaction_start() {
if (!$this->in_transaction()) {
$dbh = $this->dbh;
$sql = 'SELECT GET_LOCK("' . mysql_real_escape_string($this->lock_name, $dbh) . '", ' . $this->option_timeout . ')';
$sth = mysql_query($sql,$dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception(mysql_errno($dbh) . ": " . mysql_error($dbh));
}
list($result) = mysql_fetch_row($sth);
mysql_free_result($sth);
if (!$result) {
throw new Exception('Timeout waiting for the MySQL named lock "' . $this->lock_name . '".');
}
$this->locked = $result;
return $result;
}
return false;
}
/**
* Releases the record lock.
*
* @return boolean
*/
public function transaction_finish() {
if ($this->in_transaction()) {
$dbh = $this->dbh;
$sql = 'SELECT RELEASE_LOCK("' . mysql_real_escape_string($this->lock_name, $dbh) . '")';
$sth = mysql_query($sql,$dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception(mysql_errno($dbh) . ": " . mysql_error($dbh));
}
$this->locked = false;
return true;
}
return false;
}
/**
* Returns all data from the shared memory record.
*
* @return string
*/
public function fetch() {
$dbh = $this->dbh;
$sql = 'SELECT data FROM ' . $this->option_table . ' WHERE id="' . mysql_real_escape_string($this->key, $dbh) . '" LIMIT 1';
$sth = mysql_query($sql,$this->dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception(mysql_errno($dbh) . ": " . mysql_error($dbh));
}
list($result) = mysql_fetch_row($sth);
mysql_free_result($sth);
if (!isset($result)) {
$result = '';
}
return $result;
}
/**
* Writes the given string to the shared memory record.
*
* @param string $value
* @return integer
*/
public function store($value) {
$dbh = $this->dbh;
$sql = 'REPLACE INTO ' . $this->option_table . ' (id,data) VALUES ("' . mysql_real_escape_string($this->key, $dbh) . '", "' . mysql_real_escape_string($value, $dbh) . '")';
$sth = mysql_query($sql,$dbh);
if (!isset($sth) or ($sth === false)) {
throw new Exception(mysql_errno($dbh) . ": " . mysql_error($dbh));
}
return strlen($value);
}
}
?>