diff --git a/lib/Doctrine/Locking/Manager/Pessimistic.php b/lib/Doctrine/Locking/Manager/Pessimistic.php index 97c772b2e..455a0c426 100644 --- a/lib/Doctrine/Locking/Manager/Pessimistic.php +++ b/lib/Doctrine/Locking/Manager/Pessimistic.php @@ -93,7 +93,7 @@ public function __construct(Doctrine_Connection $conn) * Obtains a lock on a {@link Doctrine_Record} * * @param Doctrine_Record $record The record that has to be locked - * @param mixed $userIdent A unique identifier of the locking user + * @param mixed $userIdent A unique identifier for the lock. * @return boolean TRUE if the locking was successful, FALSE if another user * holds a lock on this record * @throws Doctrine_Locking_Exception If the locking failed due to database errors @@ -106,9 +106,16 @@ public function getLock(Doctrine_Record $record, $userIdent) $gotLock = false; $time = time(); + $objectKey = []; if (is_array($key)) { // Composite key + foreach ($key as $keyName) { + $objectKey[] = $record->get($keyName); + } $key = implode('|', $key); + $objectKey = implode('|', $objectKey); + } else { + $objectKey = $record->get($key); } try { @@ -120,7 +127,7 @@ public function getLock(Doctrine_Record $record, $userIdent) . ' VALUES (:object_type, :object_key, :user_ident, :ts_obtained)'); $stmt->bindParam(':object_type', $objectType); - $stmt->bindParam(':object_key', $key); + $stmt->bindParam(':object_key', $objectKey); $stmt->bindParam(':user_ident', $userIdent); $stmt->bindParam(':ts_obtained', $time); @@ -134,8 +141,8 @@ public function getLock(Doctrine_Record $record, $userIdent) } if ( ! $gotLock) { - $lockingUserIdent = $this->_getLockingUserIdent($objectType, $key); - if ($lockingUserIdent !== null && $lockingUserIdent == $userIdent) { + $lockingKey = $this->_getUserIdent($objectType, $key); + if ($lockingKey !== null && $lockingKey == $userIdent) { $gotLock = true; // The requesting user already has a lock // Update timestamp $stmt = $dbh->prepare('UPDATE ' . $this->_lockTable @@ -145,8 +152,8 @@ public function getLock(Doctrine_Record $record, $userIdent) . ' user_ident = :user_ident'); $stmt->bindParam(':ts', $time); $stmt->bindParam(':object_type', $objectType); - $stmt->bindParam(':object_key', $key); - $stmt->bindParam(':user_ident', $lockingUserIdent); + $stmt->bindParam(':object_key', $objectKey); + $stmt->bindParam(':user_ident', $lockingKey); $stmt->execute(); } } @@ -163,7 +170,7 @@ public function getLock(Doctrine_Record $record, $userIdent) * Releases a lock on a {@link Doctrine_Record} * * @param Doctrine_Record $record The record for which the lock has to be released - * @param mixed $userIdent The unique identifier of the locking user + * @param mixed $userIdent. The unique identifier for the lock * @return boolean TRUE if a lock was released, FALSE if no lock was released * @throws Doctrine_Locking_Exception If the release procedure failed due to database errors */ @@ -172,9 +179,16 @@ public function releaseLock(Doctrine_Record $record, $userIdent) $objectType = $record->getTable()->getComponentName(); $key = $record->getTable()->getIdentifier(); + $objectKey = []; if (is_array($key)) { // Composite key + foreach ($key as $keyName) { + $objectKey[] = $record->get($keyName); + } $key = implode('|', $key); + $objectKey = implode('|', $objectKey); + } else { + $objectKey = $record->get($key); } try { @@ -184,7 +198,7 @@ public function releaseLock(Doctrine_Record $record, $userIdent) object_key = :object_key AND user_ident = :user_ident"); $stmt->bindParam(':object_type', $objectType); - $stmt->bindParam(':object_key', $key); + $stmt->bindParam(':object_key', $objectKey); $stmt->bindParam(':user_ident', $userIdent); $stmt->execute(); @@ -197,14 +211,14 @@ public function releaseLock(Doctrine_Record $record, $userIdent) } /** - * Gets the unique user identifier of a lock + * Gets the unique identifier of a lock * * @param string $objectType The type of the object (component name) * @param mixed $key The unique key of the object. Can be string or array - * @return string The unique user identifier for the specified lock + * @return string The unique identifier for the specified lock * @throws Doctrine_Locking_Exception If the query failed due to database errors */ - private function _getLockingUserIdent($objectType, $key) + private function _getUserIdent($objectType, $key) { if (is_array($key)) { // Composite key @@ -242,7 +256,19 @@ public function getLockOwner($lockedRecord) { $objectType = $lockedRecord->getTable()->getComponentName(); $key = $lockedRecord->getTable()->getIdentifier(); - return $this->_getLockingUserIdent($objectType, $key); + + $objectKey = []; + if (is_array($key)) { + // Composite key + foreach ($key as $keyName) { + $objectKey[] = $lockedRecord->get($keyName); + } + $objectKey = implode('|', $objectKey); + } else { + $objectKey = $lockedRecord->get($key); + } + + return $this->_getUserIdent($objectType, $objectKey); } /** @@ -252,7 +278,7 @@ public function getLockOwner($lockedRecord) * * @param integer $age The maximum valid age of locks in seconds * @param string $objectType The type of the object (component name) - * @param mixed $userIdent The unique identifier of the locking user + * @param mixed $userIdent The unique identifier of the lock * @return integer The number of locks that have been released * @throws Doctrine_Locking_Exception If the release process failed due to database errors */ diff --git a/tests/PessimisticLockingTestCase.php b/tests/PessimisticLockingTestCase.php index 306067b78..cfeb6e8a1 100644 --- a/tests/PessimisticLockingTestCase.php +++ b/tests/PessimisticLockingTestCase.php @@ -39,15 +39,29 @@ class Doctrine_PessimisticLocking_TestCase extends Doctrine_UnitTestCase * * Creates a locking manager and a test record to work with. */ - public function testInitData() + public function setup() { + parent::setup(); + $this->lockingManager = new Doctrine_Locking_Manager_Pessimistic($this->connection); - + // Create sample data to test on $entry1 = new Forum_Entry(); $entry1->author = 'Bart Simpson'; $entry1->topic = 'I love donuts!'; $entry1->save(); + + $entry2 = new Forum_Entry(); + $entry2->author = 'Bart Simpson'; + $entry2->topic = 'I play saxophone.'; + $entry2->save(); + } + + public function tearDown() + { + parent::tearDown(); + $this->lockingManager->releaseAgedLocks(-1); // age -1 seconds => release all in prep for next test + $this->connection->query("DELETE FROM Forum_Entry WHERE Forum_Entry.author = 'Bart Simpson'"); } public function prepareTables() @@ -73,6 +87,10 @@ public function testLock() $gotLock = $this->lockingManager->getLock($entries[0], 'konstav'); $this->assertFalse($gotLock); + // Test successfully getting a lock on the second forum entry + $gotLock = $this->lockingManager->getLock($entries[1], 'michael'); + $this->assertTrue($gotLock); + // Test release lock $released = $this->lockingManager->releaseLock($entries[0], 'romanb'); $this->assertTrue($released);