Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: PropelPDO.php 1262 2009-10-26 20:54:39Z francois $
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information please see
19
 * <http://propel.phpdb.org>.
20
 */
21
 
22
 
23
/**
24
 * PDO connection subclass that provides the basic fixes to PDO that are required by Propel.
25
 *
26
 * This class was designed to work around the limitation in PDO where attempting to begin
27
 * a transaction when one has already been begun will trigger a PDOException.  Propel
28
 * relies on the ability to create nested transactions, even if the underlying layer
29
 * simply ignores these (because it doesn't support nested transactions).
30
 *
31
 * The changes that this class makes to the underlying API include the addition of the
32
 * getNestedTransactionDepth() and isInTransaction() and the fact that beginTransaction()
33
 * will no longer throw a PDOException (or trigger an error) if a transaction is already
34
 * in-progress.
35
 *
36
 * @author     Cameron Brunner <cameron.brunner@gmail.com>
37
 * @author     Hans Lellelid <hans@xmpl.org>
38
 * @author     Christian Abegg <abegg.ch@gmail.com>
39
 * @since      2006-09-22
40
 * @package    propel.util
41
 */
42
class PropelPDO extends PDO {
43
 
44
	/**
45
	 * Attribute to use to set whether to cache prepared statements.
46
	 */
47
	const PROPEL_ATTR_CACHE_PREPARES = -1;
48
 
49
	/**
50
	 * The current transaction depth.
51
	 * @var        int
52
	 */
53
	protected $nestedTransactionCount = 0;
54
 
55
	/**
56
	 * Cache of prepared statements (PDOStatement) keyed by md5 of SQL.
57
	 *
58
	 * @var        array [md5(sql) => PDOStatement]
59
	 */
60
	protected $preparedStatements = array();
61
 
62
	/**
63
	 * Whether to cache prepared statements.
64
	 *
65
	 * @var        boolean
66
	 */
67
	protected $cachePreparedStatements = false;
68
 
69
	/**
70
	 * Whether the final commit is possible
71
	 * Is false if a nested transaction is rolled back
72
	 */
73
	protected $isUncommitable = false;
74
 
75
	/**
76
	 * Gets the current transaction depth.
77
	 * @return     int
78
	 */
79
	public function getNestedTransactionCount()
80
	{
81
		return $this->nestedTransactionCount;
82
	}
83
 
84
	/**
85
	 * Set the current transaction depth.
86
	 * @param      int $v The new depth.
87
	 */
88
	protected function setNestedTransactionCount($v)
89
	{
90
		$this->nestedTransactionCount = $v;
91
	}
92
 
93
	/**
94
	 * Decrements the current transaction depth by one.
95
	 */
96
	protected function decrementNestedTransactionCount()
97
	{
98
		$this->nestedTransactionCount--;
99
	}
100
 
101
	/**
102
	 * Increments the current transaction depth by one.
103
	 */
104
	protected function incrementNestedTransactionCount()
105
	{
106
		$this->nestedTransactionCount++;
107
	}
108
 
109
	/**
110
	 * Is this PDO connection currently in-transaction?
111
	 * This is equivalent to asking whether the current nested transaction count
112
	 * is greater than 0.
113
	 * @return     boolean
114
	 */
115
	public function isInTransaction()
116
	{
117
		return ($this->getNestedTransactionCount() > 0);
118
	}
119
 
120
	/**
121
	 * Overrides PDO::beginTransaction() to prevent errors due to already-in-progress transaction.
122
	 */
123
	public function beginTransaction()
124
	{
125
		$return = true;
126
		$opcount = $this->getNestedTransactionCount();
127
		if ( $opcount === 0 ) {
128
			$return = parent::beginTransaction();
129
			$this->isUncommitable = false;
130
		}
131
		$this->incrementNestedTransactionCount();
132
		return $return;
133
	}
134
 
135
	/**
136
	 * Overrides PDO::commit() to only commit the transaction if we are in the outermost
137
	 * transaction nesting level.
138
	 */
139
	public function commit()
140
	{
141
		$return = true;
142
		$opcount = $this->getNestedTransactionCount();
143
		if ($opcount > 0) {
144
			if ($opcount === 1) {
145
				if ($this->isUncommitable) {
146
					throw new PropelException('Cannot commit because a nested transaction was rolled back');
147
				} else {
148
					$return = parent::commit();
149
				}
150
			}
151
			$this->decrementNestedTransactionCount();
152
		}
153
		return $return;
154
	}
155
 
156
	/**
157
	 * Overrides PDO::rollBack() to only rollback the transaction if we are in the outermost
158
	 * transaction nesting level
159
	 * @return     boolean Whether operation was successful.
160
	 */
161
	public function rollBack()
162
	{
163
		$return = true;
164
		$opcount = $this->getNestedTransactionCount();
165
		if ($opcount > 0) {
166
			if ($opcount === 1) {
167
				$return = parent::rollBack();
168
			} else {
169
				$this->isUncommitable = true;
170
			}
171
			$this->decrementNestedTransactionCount();
172
		}
173
		return $return;
174
	}
175
 
176
	/**
177
	* Rollback the whole transaction, even if this is a nested rollback
178
	* and reset the nested transaction count to 0.
179
	* @return     boolean Whether operation was successful.
180
	*/
181
	public function forceRollBack()
182
	{
183
		$return = true;
184
		$opcount = $this->getNestedTransactionCount();
185
		if ($opcount > 0) {
186
			// If we're in a transaction, always roll it back
187
			// regardless of nesting level.
188
			$return = parent::rollBack();
189
 
190
			// reset nested transaction count to 0 so that we don't
191
			// try to commit (or rollback) the transaction outside this scope.
192
			$this->nestedTransactionCount = 0;
193
		}
194
		return $return;
195
	}
196
 
197
	/**
198
	 * Sets a connection attribute.
199
	 *
200
	 * This is overridden here to provide support for setting Propel-specific attributes
201
	 * too.
202
	 *
203
	 * @param      int $attribute The attribute to set (e.g. PropelPDO::PROPEL_ATTR_CACHE_PREPARES).
204
	 * @param      mixed $value The attribute value.
205
	 */
206
	public function setAttribute($attribute, $value)
207
	{
208
		switch($attribute) {
209
			case self::PROPEL_ATTR_CACHE_PREPARES:
210
				$this->cachePreparedStatements = $value;
211
				break;
212
			default:
213
				parent::setAttribute($attribute, $value);
214
		}
215
	}
216
 
217
	/**
218
	 * Gets a connection attribute.
219
	 *
220
	 * This is overridden here to provide support for setting Propel-specific attributes
221
	 * too.
222
	 *
223
	 * @param      int $attribute The attribute to get (e.g. PropelPDO::PROPEL_ATTR_CACHE_PREPARES).
224
	 */
225
	public function getAttribute($attribute)
226
	{
227
		switch($attribute) {
228
			case self::PROPEL_ATTR_CACHE_PREPARES:
229
				return $this->cachePreparedStatements;
230
				break;
231
			default:
232
				return parent::getAttribute($attribute);
233
		}
234
	}
235
 
236
	/**
237
	 * Overrides PDO::prepare() to add query caching support if the
238
	 * PropelPDO::PROPEL_ATTR_CACHE_PREPARES was set to true.
239
	 * .
240
	 * @param      string $sql
241
	 * @param      array
242
	 * @return     PDOStatement
243
	 */
244
	public function prepare($sql, $driver_options = array())
245
	{
246
		if ($this->cachePreparedStatements) {
247
			$key = $sql;
248
			if (!isset($this->preparedStatements[$key])) {
249
				$stmt = parent::prepare($sql, $driver_options);
250
				$this->preparedStatements[$key] = $stmt;
251
				return $stmt;
252
			} else {
253
				return $this->preparedStatements[$key];
254
			}
255
		} else {
256
			return parent::prepare($sql, $driver_options);
257
		}
258
	}
259
 
260
	/**
261
	 * Clears any stored prepared statements for this connection.
262
	 */
263
	public function clearStatementCache()
264
	{
265
		$this->preparedStatements = array();
266
	}
267
 
268
}