I just started on a project that I knew was going to need some sort of data lock that would prevent 2 users from editing the same data at the same time. At first I thought that i would have to write a function that behaved like the C function flock(). I ran across this forum post http://www.dmcinsights.com/phorum/read.php?13,30233,30260 which I have to give credit to is the inspiration for the code below.
In my setup I have a master include file that is included every time the script runs. It checks the session to see if a lock code has been assigned to the use. If so then it declares DB_LOCK_KEY as a constant
//### setup the lock key for locking db rows #### if(empty($_SESSION['lock_key'])){ $lock_key=mkUniqueCode();//see below for function declaration $_SESSION['lock_key']=$lock_key; define('DB_LOCK_KEY',$lock_key); }else{ define('DB_LOCK_KEY',$_SESSION['lock_key']); }
There are a couple of functions used in dbLockRow() that aren't standard php functions. Here is the run down of what they do
- dbQuery() - queries the database | just swap it out with your own function
- addMessage() - is a function that add a string to a session variable
- location() - same as:session_write_close(); header('location: '.$location);
Here is a sample a table that this function could be used
CREATE TABLE `content_location` ( `content_location_id` int(11) NOT NULL auto_increment, `content_location_title` varchar(255) NOT NULL default '', `content_location_desc` text NOT NULL, `content_location_lock_key` varchar(255) NOT NULL default '', `content_location_lock_expiry_time` timestamp NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`content_location_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
I have modified the query a little bit from the forum post. I added this: OR {$table}_lock_key='" . DB_LOCK_KEY . "' AND {$table}_id=" . $id This allows the same user to access the row before the lock_expiry_time runs out. I added it because if the user chose to edit a row and accidentally hit the back button they would be locked out of the row.
/** * dbLockRow() * * @author Jesse Baird <jebaird@gmail.com> * @version .01 * @since 1.3 * * @param string $table | name of the target table * @param integer $id * @param string $dataTypeDesc | user, setting, claim etc * @param bool $sendHeaders | true add message and send the user to $location * @param string $message | $dataTypeDesc and $duration are tacked on the begianing and end of this string * @param string $location | name of the php file that you would like the user to be directed to if locking fails * @param string $duration | mysql timestamp for how long you would like the row to be locked * @return */ function dbLockRow($table = '', $id = 0, $dataTypeDesc = '', $sendHeaders = true, $location = this_php, $duration = '00:05:00', $message = 'is currently being edited by another user. Please check try again in') { $res = dbQuery("UPDATE $table SET {$table}_lock_expiry_time = ADDTIME(NOW(),'" . $duration . "'),{$table}_lock_key='" . DB_LOCK_KEY . "' WHERE NOW() > {$table}_lock_expiry_time AND {$table}_id=" . $id ." OR {$table}_lock_key='" . DB_LOCK_KEY . "' AND {$table}_id=" . $id); if(mysql_affected_rows() == 0) { //lock failed - let the user know that they can't get access to the record right now if($sendHeaders) { addMessage('This '.$dataTypeDesc . ' ' . $message . ' ' . $duration); //let the user know that the row is locked location($location); // send the user to a different url } else return false; } else { //echo 'row locked'; return true; } } /** * mkUniqueCode() * @uses creates a random sting of chars and nums based on the md5 hash of unipid(rand()) * @author unknown * @param integer $length * @return mixed */ function mkUniqueCode($length = 10) { $code = md5(uniqid(rand(), true)); if($length != "") return substr($code, 0, $length); else return $code; }