<?php
// +----------------------------------------------------------------------+
// | Simple and safe MySQL lock manager 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: MysqlLocker.php,v 1.1 2005/02/14 23:49:33 cmanley Exp $
//
/**
* @author Craig Manley (freelance software developer)
* @copyright Copyright © 2005, Craig Manley. All rights reserved.
* @package MysqlLocker
* @version $Revision: 1.1 $
*/
/**
* This is a simple class for safely using MySQL locks.
* Locks are created when you instantiate the class and are automatically released when
* the object is destroyed. This occurs when the object is not longer referenced,
* or when it goes out of scope. The latter also occurs when a PHP script dies or
* when an exception occurs, and this is where this class is most useful because
* it'll make sure that all existing locks are automatically released for you.
*
* <b>SYNOPSIS</b>
* <pre>
* require_once('MysqlLocker.php');
*
* // Create table locks
* $locker = new MysqlLocker($link, array('Customers' => 'READ',
* 'Articles' => 'WRITE'));
*
* // Execute some tricky statements here...
* // ... and then ...
* throw new Exception('Nooooooooo!!!!');
* // ... or ...
* trigger_error('Oops something bad happened!', E_USER_ERROR);
* // ... or ...
* die("Uuuhghhgg!\n");
*
* // Locks are also released when $locker goes out of scope or if you explictly set it to null.
* $locker = null;
* </pre>
*
* @author Craig Manley (freelance software developer)
* @copyright Copyright © 2005, Craig Manley. All rights reserved.
* @package MysqlLocker
* @version $Revision: 1.1 $
*/
class MysqlLocker {
// Private members
private $dbh = null;
private $option_debug = false;
/**
* Contructor. Locks the specified tables and keeps them locked for the life time of this object.
*
* @param resource $link mysql link resource connected to the database.
* @param array $locks Associative array of tablename => locktype pairs.
* @param array $options Optional associative array of options:
* <pre>
* debug: if set and true, then debug messages are passed to the error_log() function.
* </pre>
* @return object
*/
public function __construct($link = null, $locks = null, $options = null) {
// Get parameter 1.
if (!(isset($link) && is_resource($link) && (get_resource_type($link) == 'mysql link'))) {
throw new Exception('First parameter is not a mysql link resource!');
}
$this->dbh = $link;
// Get parameter 2.
if (!isset($locks) && is_array($locks) && count($locks)) {
throw new Exception('No table locks specified!');
}
$tables = array_keys($locks);
if (!count($tables)) {
throw new Exception('No table locks specified!');
}
// Get parameter 3.
if (isset($options) && is_array($options)) {
if (isset($options['debug']) && $options['debug']) {
$this->option_debug = true;
}
}
// Lock tables.
$pairs = array();
foreach ($tables as $table) {
array_push($pairs, "$table " . $locks[$table]);
}
$sql = 'LOCK TABLES ' . implode(', ', $pairs);
$this->option_debug && error_log(__METHOD__ . " locking tables with SQL \"$sql\".");
if (!mysql_query($sql)) {
throw new Exception('Error locking tables; code: ' . mysql_errno($dbh) . "; message: " . mysql_error($dbh));
}
}
/**
* Destructor. Unlocks the locked tables.
*/
public function __destruct() {
$dbh = $this->dbh;
if (isset($dbh)) {
$this->option_debug && error_log(__METHOD__ . ' unlocking tables.');
if (!mysql_query('UNLOCK TABLES', $dbh)) {
$this->option_debug && error_log(__METHOD__ . ' failed to unlock tables, attempting to reconnect to MySQL.');
if (mysql_ping($dbh)) {
$this->option_debug && error_log(__METHOD__ . ' about to unlock tables (2nd attempt).');
if (!mysql_query('UNLOCK TABLES', $dbh)) {
trigger_error("Failed to unlock tables, even after reconnect.\nMySQL code: " . mysql_errno($dbh) . "\nMySQL reason: " . mysql_error($dbh), E_USER_WARNING); // this should never occur.
}
}
else {
trigger_error("Failed to unlock tables. Unable to reconnect to the MySQL server.\nMySQL code: " . mysql_errno($dbh) . "\nMySQL reason: " . mysql_error($dbh), E_USER_NOTICE); // not a warning because the locks have probably been released anyway due to the disconnect.
}
}
}
}
}
?>