|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Christian Kuhn <lolli@schwarzbu.ch> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 00025 /** 00026 * A PDO database cache backend 00027 * 00028 * @package TYPO3 00029 * @subpackage t3lib_cache 00030 * @api 00031 * @scope prototype 00032 * @author Christian Kuhn <lolli@schwarzbu.ch> 00033 * @version $Id$ 00034 */ 00035 class t3lib_cache_backend_PdoBackend extends t3lib_cache_backend_AbstractBackend { 00036 00037 /** 00038 * @var string 00039 */ 00040 protected $dataSourceName; 00041 00042 /** 00043 * @var string 00044 */ 00045 protected $username; 00046 00047 /** 00048 * @var string 00049 */ 00050 protected $password; 00051 00052 /** 00053 * Used to seperate stored data by user, SAPI, context, ... 00054 * @var string 00055 */ 00056 protected $scope; 00057 00058 /** 00059 * @var PDO 00060 */ 00061 protected $databaseHandle; 00062 00063 /** 00064 * @var string 00065 */ 00066 protected $pdoDriver; 00067 00068 /** 00069 * Constructs this backend 00070 * 00071 * @param array $options Configuration options - depends on the actual backend 00072 * @author Christian Kuhn <lolli@schwarzbu.ch> 00073 */ 00074 public function __construct(array $options = array()) { 00075 parent::__construct($options); 00076 00077 $this->connect(); 00078 } 00079 00080 /** 00081 * Sets the DSN to use 00082 * 00083 * @param string $DSN The DSN to use for connecting to the DB 00084 * @return void 00085 * @author Karsten Dambekalns <karsten@typo3.org> 00086 * @api 00087 */ 00088 public function setDataSourceName($DSN) { 00089 $this->dataSourceName = $DSN; 00090 } 00091 00092 /** 00093 * Sets the username to use 00094 * 00095 * @param string $username The username to use for connecting to the DB 00096 * @return void 00097 * @author Karsten Dambekalns <karsten@typo3.org> 00098 * @api 00099 */ 00100 public function setUsername($username) { 00101 $this->username = $username; 00102 } 00103 00104 /** 00105 * Sets the password to use 00106 * 00107 * @param string $password The password to use for connecting to the DB 00108 * @return void 00109 * @author Karsten Dambekalns <karsten@typo3.org> 00110 * @api 00111 */ 00112 public function setPassword($password) { 00113 $this->password = $password; 00114 } 00115 00116 /** 00117 * Initializes the identifier prefix when setting the cache. 00118 * 00119 * @param t3lib_cache_frontend_Frontend $cache 00120 * @return void 00121 * @author Robert Lemke <robert@typo3.org> 00122 * @author Karsten Dambekalns <karsten@typo3.org> 00123 */ 00124 public function setCache(t3lib_cache_frontend_Frontend $cache) { 00125 parent::setCache($cache); 00126 $processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default'); 00127 $this->scope = t3lib_div::shortMD5(PATH_site . $processUser['name'], 12); 00128 } 00129 00130 /** 00131 * Saves data in the cache. 00132 * 00133 * @param string $entryIdentifier An identifier for this specific cache entry 00134 * @param string $data The data to be stored 00135 * @param array $tags Tags to associate with this cache entry 00136 * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime. 00137 * @return void 00138 * @throws t3lib_cache_Exception if no cache frontend has been set. 00139 * @throws t3lib_cache_exception_InvalidData if $data is not a string 00140 * @author Karsten Dambekalns <karsten@typo3.org> 00141 * @api 00142 */ 00143 public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) { 00144 if (!$this->cache instanceof t3lib_cache_frontend_Frontend) { 00145 throw new t3lib_cache_Exception( 00146 'No cache frontend has been set yet via setCache().', 00147 1259515600 00148 ); 00149 } 00150 00151 if (!is_string($data)) { 00152 throw new t3lib_cache_exception_InvalidData( 00153 'The specified data is of type "' . gettype($data) . '" but a string is expected.', 00154 1259515601 00155 ); 00156 } 00157 00158 if ($this->has($entryIdentifier)) { 00159 $this->remove($entryIdentifier); 00160 } 00161 00162 $lifetime = ($lifetime === NULL) ? $this->defaultLifetime : $lifetime; 00163 00164 $statementHandle = $this->databaseHandle->prepare( 00165 'INSERT INTO "cache" ("identifier", "scope", "cache", "created", "lifetime", "content") VALUES (?, ?, ?, ?, ?, ?)' 00166 ); 00167 $result = $statementHandle->execute( 00168 array($entryIdentifier, $this->scope, $this->cacheIdentifier, $GLOBALS['EXEC_TIME'], $lifetime, $data) 00169 ); 00170 00171 if ($result === FALSE) { 00172 throw new t3lib_cache_Exception( 00173 'The cache entry "' . $entryIdentifier . '" could not be written.', 00174 1259530791 00175 ); 00176 } 00177 00178 $statementHandle = $this->databaseHandle->prepare( 00179 'INSERT INTO "tags" ("identifier", "scope", "cache", "tag") VALUES (?, ?, ?, ?)' 00180 ); 00181 00182 foreach ($tags as $tag) { 00183 $result = $statementHandle->execute( 00184 array($entryIdentifier, $this->scope, $this->cacheIdentifier, $tag) 00185 ); 00186 if ($result === FALSE) { 00187 throw new t3lib_cache_Exception( 00188 'The tag "' . $tag . ' for cache entry "' . $entryIdentifier . '" could not be written.', 00189 1259530751 00190 ); 00191 } 00192 } 00193 } 00194 00195 /** 00196 * Loads data from the cache. 00197 * 00198 * @param string $entryIdentifier An identifier which describes the cache entry to load 00199 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded 00200 * @author Karsten Dambekalns <karsten@typo3.org> 00201 * @api 00202 */ 00203 public function get($entryIdentifier) { 00204 $statementHandle = $this->databaseHandle->prepare( 00205 'SELECT "content" FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?' . $this->getNotExpiredStatement() 00206 ); 00207 $statementHandle->execute( 00208 array($entryIdentifier, $this->scope, $this->cacheIdentifier) 00209 ); 00210 return $statementHandle->fetchColumn(); 00211 } 00212 00213 /** 00214 * Checks if a cache entry with the specified identifier exists. 00215 * 00216 * @param string $entryIdentifier An identifier specifying the cache entry 00217 * @return boolean TRUE if such an entry exists, FALSE if not 00218 * @author Karsten Dambekalns <karsten@typo3.org> 00219 * @api 00220 */ 00221 public function has($entryIdentifier) { 00222 $statementHandle = $this->databaseHandle->prepare( 00223 'SELECT COUNT("identifier") FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?' . $this->getNotExpiredStatement() 00224 ); 00225 $statementHandle->execute( 00226 array($entryIdentifier, $this->scope, $this->cacheIdentifier) 00227 ); 00228 return ($statementHandle->fetchColumn() > 0); 00229 } 00230 00231 /** 00232 * Removes all cache entries matching the specified identifier. 00233 * Usually this only affects one entry but if - for what reason ever - 00234 * old entries for the identifier still exist, they are removed as well. 00235 * 00236 * @param string $entryIdentifier Specifies the cache entry to remove 00237 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found 00238 * @author Karsten Dambekalns <karsten@typo3.org> 00239 * @api 00240 */ 00241 public function remove($entryIdentifier) { 00242 $statementHandle = $this->databaseHandle->prepare( 00243 'DELETE FROM "tags" WHERE "identifier"=? AND "scope"=? AND "cache"=?' 00244 ); 00245 $statementHandle->execute( 00246 array($entryIdentifier, $this->scope, $this->cacheIdentifier) 00247 ); 00248 00249 $statementHandle = $this->databaseHandle->prepare( 00250 'DELETE FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?' 00251 ); 00252 $statementHandle->execute( 00253 array($entryIdentifier, $this->scope, $this->cacheIdentifier) 00254 ); 00255 00256 return ($statementHandle->rowCount() > 0); 00257 } 00258 00259 /** 00260 * Removes all cache entries of this cache. 00261 * 00262 * @return void 00263 * @author Karsten Dambekalns <karsten@typo3.org> 00264 * @api 00265 */ 00266 public function flush() { 00267 $statementHandle = $this->databaseHandle->prepare( 00268 'DELETE FROM "tags" WHERE "scope"=? AND "cache"=?' 00269 ); 00270 $statementHandle->execute( 00271 array($this->scope, $this->cacheIdentifier) 00272 ); 00273 00274 $statementHandle = $this->databaseHandle->prepare( 00275 'DELETE FROM "cache" WHERE "scope"=? AND "cache"=?' 00276 ); 00277 $statementHandle->execute( 00278 array($this->scope, $this->cacheIdentifier) 00279 ); 00280 } 00281 00282 /** 00283 * Removes all cache entries of this cache which are tagged by the specified tag. 00284 * 00285 * @param string $tag The tag the entries must have 00286 * @return void 00287 * @author Robert Lemke <robert@typo3.org> 00288 * @api 00289 */ 00290 public function flushByTag($tag) { 00291 $statementHandle = $this->databaseHandle->prepare( 00292 'DELETE FROM "cache" WHERE "scope"=? AND "cache"=? AND "identifier" IN (SELECT "identifier" FROM "tags" WHERE "scope"=? AND "cache"=? AND "tag"=?)' 00293 ); 00294 $statementHandle->execute( 00295 array($this->scope, $this->cacheIdentifier, $this->scope, $this->cacheIdentifier, $tag) 00296 ); 00297 00298 $statementHandle = $this->databaseHandle->prepare( 00299 'DELETE FROM "tags" WHERE "scope"=? AND "cache"=? AND "tag"=?' 00300 ); 00301 $statementHandle->execute( 00302 array($this->scope, $this->cacheIdentifier, $tag) 00303 ); 00304 } 00305 00306 /** 00307 * Removes all cache entries of this cache which are tagged by the specified tags. 00308 * This method doesn't exist in FLOW3, but is mandatory for TYPO3v4. 00309 * 00310 * @TODO: Make smarter 00311 * @param array $tags The tags the entries must have 00312 * @return void 00313 * @author Christian Kuhn <lolli@schwarzbu.ch> 00314 */ 00315 public function flushBytags(array $tags) { 00316 foreach ($tags as $tag) { 00317 $this->flushByTag($tag); 00318 } 00319 } 00320 00321 /** 00322 * Finds and returns all cache entry identifiers which are tagged by the 00323 * specified tag. 00324 * 00325 * @param string $tag The tag to search for 00326 * @return array An array with identifiers of all matching entries. An empty array if no entries matched 00327 * @author Karsten Dambekalns <karsten@typo3.org> 00328 * @api 00329 */ 00330 public function findIdentifiersByTag($tag) { 00331 $statementHandle = $this->databaseHandle->prepare( 00332 'SELECT "identifier" FROM "tags" WHERE "scope"=? AND "cache"=? AND "tag"=?' 00333 ); 00334 $statementHandle->execute( 00335 array($this->scope, $this->cacheIdentifier, $tag) 00336 ); 00337 return $statementHandle->fetchAll(PDO::FETCH_COLUMN); 00338 } 00339 00340 /** 00341 * Finds and returns all cache entry identifiers which are tagged with 00342 * all of the specified tags. 00343 * This method doesn't exist in FLOW3, but is mandatory for TYPO3v4. 00344 * 00345 * @TODO: Make smarter 00346 * @param array $tags Tags to search for 00347 * @return array An array with identifiers of all matching entries. An empty array if no entries matched 00348 * @author Christian Kuhn <lolli@schwarzbu.ch> 00349 */ 00350 public function findIdentifiersByTags(array $tags) { 00351 $taggedEntries = array(); 00352 $foundEntries = array(); 00353 00354 foreach ($tags as $tag) { 00355 $taggedEntries[$tag] = $this->findIdentifiersByTag($tag); 00356 } 00357 00358 $intersectedTaggedEntries = call_user_func_array('array_intersect', $taggedEntries); 00359 00360 foreach ($intersectedTaggedEntries as $entryIdentifier) { 00361 if ($this->has($entryIdentifier)) { 00362 $foundEntries[$entryIdentifier] = $entryIdentifier; 00363 } 00364 } 00365 00366 return $foundEntries; 00367 } 00368 00369 /** 00370 * Does garbage collection 00371 * 00372 * @return void 00373 * @author Karsten Dambekalns <karsten@typo3.org> 00374 * @api 00375 */ 00376 public function collectGarbage() { 00377 $statementHandle = $this->databaseHandle->prepare( 00378 'DELETE FROM "tags" WHERE "scope"=? AND "cache"=? AND "identifier" IN ' . 00379 '(SELECT "identifier" FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME'] . ')' 00380 ); 00381 $statementHandle->execute( 00382 array($this->scope, $this->cacheIdentifier, $this->scope, $this->cacheIdentifier) 00383 ); 00384 00385 $statementHandle = $this->databaseHandle->prepare( 00386 'DELETE FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME'] 00387 ); 00388 $statementHandle->execute( 00389 array($this->scope, $this->cacheIdentifier) 00390 ); 00391 } 00392 00393 /** 00394 * Returns an SQL statement that evaluates to true if the entry is not expired. 00395 * 00396 * @return string 00397 * @author Karsten Dambekalns <karsten@typo3.org> 00398 */ 00399 protected function getNotExpiredStatement() { 00400 return ' AND ("lifetime" = 0 OR "created" + "lifetime" >= ' . $GLOBALS['EXEC_TIME'] . ')'; 00401 } 00402 00403 /** 00404 * Connect to the database 00405 * 00406 * @return void 00407 * @author Karsten Dambekalns <karsten@typo3.org> 00408 */ 00409 protected function connect() { 00410 try { 00411 $splitdsn = explode(':', $this->dataSourceName, 2); 00412 $this->pdoDriver = $splitdsn[0]; 00413 00414 if ($this->pdoDriver === 'sqlite' && !file_exists($splitdsn[1])) { 00415 $this->databaseHandle = t3lib_div::makeInstance('PDO', $this->dataSourceName, $this->username, $this->password); 00416 $this->createCacheTables(); 00417 } else { 00418 $this->databaseHandle = t3lib_div::makeInstance('PDO', $this->dataSourceName, $this->username, $this->password); 00419 } 00420 00421 $this->databaseHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 00422 00423 if ($this->pdoDriver === 'mysql') { 00424 $this->databaseHandle->exec('SET SESSION sql_mode=\'ANSI\';'); 00425 } 00426 } catch (PDOException $e) { 00427 } 00428 } 00429 00430 /** 00431 * Creates the tables needed for the cache backend. 00432 * 00433 * @return void 00434 * @throws RuntimeException if something goes wrong 00435 * @author Karsten Dambekalns <karsten@typo3.org> 00436 */ 00437 protected function createCacheTables() { 00438 try { 00439 t3lib_PdoHelper::importSql($this->databaseHandle, $this->pdoDriver, PATH_t3lib . 'cache/backend/resources/ddl.sql'); 00440 } catch (PDOException $e) { 00441 throw new RuntimeException( 00442 'Could not create cache tables with DSN "' . $this->dataSourceName . '". PDO error: ' . $e->getMessage(), 00443 1259576985 00444 ); 00445 } 00446 } 00447 } 00448 00449 if (defined('TYPO3_MODE') && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php']) { 00450 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php']); 00451 } 00452 ?>
1.8.0