Overview

Packages

  • Lock

Classes

  • Horde_Lock
  • Horde_Lock_Exception
  • Horde_Lock_Null
  • Horde_Lock_Sql
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Lock_Sql driver implements a storage backend for the Horde_Lock
  4:  * API.
  5:  *
  6:  * The table structure for the locks is as follows:
  7:  * <pre>
  8:  * CREATE TABLE horde_locks (
  9:  *     lock_id                  VARCHAR(36) NOT NULL,
 10:  *     lock_owner               VARCHAR(32) NOT NULL,
 11:  *     lock_scope               VARCHAR(32) NOT NULL,
 12:  *     lock_principal           VARCHAR(255) NOT NULL,
 13:  *     lock_origin_timestamp    BIGINT NOT NULL,
 14:  *     lock_update_timestamp    BIGINT NOT NULL,
 15:  *     lock_expiry_timestamp    BIGINT NOT NULL,
 16:  *     lock_type                TINYINT NOT NULL,
 17:  *
 18:  *     PRIMARY KEY (lock_id)
 19:  * );
 20:  * </pre>
 21:  *
 22:  * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
 23:  *
 24:  * See the enclosed file COPYING for license information (LGPL). If you did
 25:  * not receive this file, see http://www.horde.org/licenses/lgpl21.
 26:  *
 27:  * @author   Ben Klang <bklang@horde.org>
 28:  * @category Horde
 29:  * @package  Lock
 30:  */
 31: class Horde_Lock_Sql extends Horde_Lock
 32: {
 33:     /**
 34:      * Handle for the current database connection.
 35:      *
 36:      * @var Horde_Db_Adapter
 37:      */
 38:     private $_db;
 39: 
 40:     /**
 41:      * Constructor.
 42:      *
 43:      * @param array $params  Parameters:
 44:      * <pre>
 45:      * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
 46:      * 'table' - (string) The name of the lock table in 'database'.
 47:      *           DEFAULT: 'horde_locks'
 48:      * </pre>
 49:      *
 50:      * @throws Horde_Lock_Exception
 51:      */
 52:     public function __construct($params = array())
 53:     {
 54:         if (!isset($params['db'])) {
 55:             throw new Horde_Lock_Exception('Missing db parameter.');
 56:         }
 57:         $this->_db = $params['db'];
 58:         unset($params['db']);
 59: 
 60:         $params = array_merge(array(
 61:             'table' => 'horde_locks'
 62:         ), $params);
 63: 
 64:         parent::__construct($params);
 65: 
 66:         /* Only do garbage collection if asked for, and then only 0.1% of the
 67:          * time we create an object. */
 68:         if (rand(0, 999) == 0) {
 69:             register_shutdown_function(array($this, 'doGC'));
 70:         }
 71:     }
 72: 
 73:     /**
 74:      * Return an array of information about the requested lock.
 75:      *
 76:      * @see Horde_Lock_Base::getLockInfo()
 77:      */
 78:     public function getLockInfo($lockid)
 79:     {
 80:         $now = time();
 81:         $sql = 'SELECT lock_id, lock_owner, lock_scope, lock_principal, ' .
 82:                'lock_origin_timestamp, lock_update_timestamp, ' .
 83:                'lock_expiry_timestamp, lock_type FROM ' . $this->_params['table'] .
 84:                ' WHERE lock_id = ? AND lock_expiry_timestamp >= ?';
 85:         $values = array($lockid, $now);
 86: 
 87:         try {
 88:             return $this->_db->selectOne($sql, $values);
 89:         } catch (Horde_Db_Exception $e) {
 90:             throw new Horde_Lock_Exception($e);
 91:         }
 92:     }
 93: 
 94:     /**
 95:      * Return a list of valid locks with the option to limit the results
 96:      * by principal, scope and/or type.
 97:      *
 98:      * @see Horde_Lock_Base::getLocks()
 99:      */
100:     public function getLocks($scope = null, $principal = null, $type = null)
101:     {
102:         $now = time();
103:         $sql = 'SELECT lock_id, lock_owner, lock_scope, lock_principal, ' .
104:                'lock_origin_timestamp, lock_update_timestamp, ' .
105:                'lock_expiry_timestamp, lock_type FROM ' .
106:                $this->_params['table'] . ' WHERE lock_expiry_timestamp >= ?';
107:         $values = array($now);
108: 
109:         // Check to see if we need to filter the results
110:         if (!empty($principal)) {
111:             $sql .= ' AND lock_principal = ?';
112:             $values[] = $principal;
113:         }
114:         if (!empty($scope)) {
115:             $sql .= ' AND lock_scope = ?';
116:             $values[] = $scope;
117:         }
118:         if (!empty($type)) {
119:             $sql .= ' AND lock_type = ?';
120:             $values[] = $type;
121:         }
122: 
123:         try {
124:             $result = $this->_db->selectAll($sql, $values);
125:         } catch (Horde_Db_Exception $e) {
126:             throw new Horde_Lock_Exception($e);
127:         }
128: 
129:         $locks = array();
130:         foreach ($result as $row) {
131:             $locks[$row['lock_id']] = $row;
132:         }
133: 
134:         return $locks;
135:     }
136: 
137:     /**
138:      * Extend the valid lifetime of a valid lock to now + $newtimeout.
139:      *
140:      * @see Horde_Lock_Base::resetLock()
141:      */
142:     public function resetLock($lockid, $extend)
143:     {
144:         $now = time();
145: 
146:         if (!$this->getLockInfo($lockid)) {
147:             return false;
148:         }
149: 
150:         $expiry = $now + $extend;
151:         $sql = 'UPDATE ' . $this->_params['table'] . ' SET ' .
152:                'lock_update_timestamp = ?, lock_expiry_timestamp = ? ' .
153:                'WHERE lock_id = ?';
154:         $values = array($now, $expiry, $lockid);
155: 
156:         try {
157:             $this->_db->update($sql, $values);
158:         } catch (Horde_Db_Exception $e) {
159:             throw new Horde_Lock_Exception($e);
160:         }
161: 
162:         return true;
163:     }
164: 
165:     /**
166:      * Sets a lock on the requested principal and returns the generated lock
167:      * ID.
168:      *
169:      * @see Horde_Lock_Base::setLock()
170:      */
171:     public function setLock($requestor, $scope, $principal,
172:                             $lifetime = 1, $type = Horde_Lock::TYPE_SHARED)
173:     {
174:         $oldlocks = $this->getLocks($scope, $principal, Horde_Lock::TYPE_EXCLUSIVE);
175: 
176:         if (count($oldlocks) != 0) {
177:             // An exclusive lock exists.  Deny the new request.
178:             if ($this->_logger) {
179:                 $this->_logger->log(sprintf('Lock requested for %s denied due to existing exclusive lock.', $principal), 'NOTICE');
180:             }
181:             return false;
182:         }
183: 
184:         $lockid = (string)new Horde_Support_Uuid();
185: 
186:         $now = time();
187:         $expiration = $now + $lifetime;
188:         $sql = 'INSERT INTO ' . $this->_params['table'] . ' (lock_id, lock_owner, lock_scope, lock_principal, lock_origin_timestamp, lock_update_timestamp, lock_expiry_timestamp, lock_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
189:         $values = array($lockid, $requestor, $scope, $principal, $now, $now,
190:                         $expiration, $type);
191: 
192:         try {
193:             $this->_db->insert($sql, $values);
194:         } catch (Horde_Db_Exception $e) {
195:             throw new Horde_Lock_Exception($e);
196:         }
197: 
198:         if ($this->_logger) {
199:             $this->_logger->log(sprintf('Lock %s set successfully by %s in scope %s on "%s"', $lockid, $requestor, $scope, $principal), 'DEBUG');
200:         }
201: 
202:         return $lockid;
203:     }
204: 
205:     /**
206:      * Removes a lock given the lock ID.
207:      *
208:      * @see Horde_Lock_Base::clearLock()
209:      */
210:     public function clearLock($lockid)
211:     {
212:         if (empty($lockid)) {
213:             throw new Horde_Lock_Exception('Must supply a valid lock ID.');
214:         }
215: 
216:         // Since we're trying to clear the lock we don't care
217:         // whether it is still valid or not.  Unconditionally
218:         // remove it.
219:         $sql = 'DELETE FROM ' . $this->_params['table'] . ' WHERE lock_id = ?';
220:         $values = array($lockid);
221: 
222:         try {
223:             $this->_db->delete($sql, $values);
224:         } catch (Horde_Db_Exception $e) {
225:             throw new Horde_Lock_Exception($e);
226:         }
227: 
228:         if ($this->_logger) {
229:             $this->_logger->log(sprintf('Lock %s cleared successfully.', $lockid), 'DEBUG');
230:         }
231: 
232:         return true;
233:     }
234: 
235:     /**
236:      * Do garbage collection needed for the driver.
237:      */
238:     public function doGC()
239:     {
240:         $now = time();
241:         $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE ' .
242:                  'lock_expiry_timestamp < ? AND lock_expiry_timestamp != 0';
243:         $values = array($now);
244: 
245:         try {
246:             $result = $this->_db->delete($query, $values);
247:             if ($this->_logger) {
248:                 $this->_logger->log(sprintf('Lock garbage collection cleared %d locks.', $result), 'DEBUG');
249:             }
250:         } catch (Horde_Db_Exception $e) {}
251:     }
252: 
253: }
254: 
API documentation generated by ApiGen