[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Cache / Backend / PdoBackend.php
1 <?php
2 namespace TYPO3\CMS\Core\Cache\Backend;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 /**
17 * A PDO database cache backend
18 *
19 * @author Christian Kuhn <lolli@schwarzbu.ch>
20 * @author Karsten Dambekalns <karsten@typo3.org>
21 * @api
22 */
23 class PdoBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implements \TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface {
24
25 /**
26 * @var string
27 */
28 protected $dataSourceName;
29
30 /**
31 * @var string
32 */
33 protected $username;
34
35 /**
36 * @var string
37 */
38 protected $password;
39
40 /**
41 * @var \PDO
42 */
43 protected $databaseHandle;
44
45 /**
46 * @var string
47 */
48 protected $pdoDriver;
49
50 /**
51 * Sets the DSN to use
52 *
53 * @param string $DSN The DSN to use for connecting to the DB
54 * @return void
55 * @api
56 */
57 public function setDataSourceName($DSN) {
58 $this->dataSourceName = $DSN;
59 }
60
61 /**
62 * Sets the username to use
63 *
64 * @param string $username The username to use for connecting to the DB
65 * @return void
66 * @api
67 */
68 public function setUsername($username) {
69 $this->username = $username;
70 }
71
72 /**
73 * Sets the password to use
74 *
75 * @param string $password The password to use for connecting to the DB
76 * @return void
77 * @api
78 */
79 public function setPassword($password) {
80 $this->password = $password;
81 }
82
83 /**
84 * Initialize the cache backend.
85 *
86 * @return void
87 */
88 public function initializeObject() {
89 $this->connect();
90 }
91
92 /**
93 * Saves data in the cache.
94 *
95 * @param string $entryIdentifier An identifier for this specific cache entry
96 * @param string $data The data to be stored
97 * @param array $tags Tags to associate with this cache entry
98 * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
99 * @return void
100 * @throws \TYPO3\CMS\Core\Cache\Exception if no cache frontend has been set.
101 * @throws \InvalidArgumentException if the identifier is not valid
102 * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException if $data is not a string
103 * @api
104 */
105 public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
106 if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
107 throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1259515600);
108 }
109 if (!is_string($data)) {
110 throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1259515601);
111 }
112 $this->remove($entryIdentifier);
113 $lifetime = $lifetime === NULL ? $this->defaultLifetime : $lifetime;
114 $statementHandle = $this->databaseHandle->prepare('INSERT INTO "cache" ("identifier", "context", "cache", "created", "lifetime", "content") VALUES (?, ?, ?, ?, ?, ?)');
115 $result = $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier, $GLOBALS['EXEC_TIME'], $lifetime, $data));
116 if ($result === FALSE) {
117 throw new \TYPO3\CMS\Core\Cache\Exception('The cache entry "' . $entryIdentifier . '" could not be written.', 1259530791);
118 }
119 $statementHandle = $this->databaseHandle->prepare('INSERT INTO "tags" ("identifier", "context", "cache", "tag") VALUES (?, ?, ?, ?)');
120 foreach ($tags as $tag) {
121 $result = $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier, $tag));
122 if ($result === FALSE) {
123 throw new \TYPO3\CMS\Core\Cache\Exception('The tag "' . $tag . ' for cache entry "' . $entryIdentifier . '" could not be written.', 1259530751);
124 }
125 }
126 }
127
128 /**
129 * Loads data from the cache.
130 *
131 * @param string $entryIdentifier An identifier which describes the cache entry to load
132 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
133 * @api
134 */
135 public function get($entryIdentifier) {
136 $statementHandle = $this->databaseHandle->prepare('SELECT "content" FROM "cache" WHERE "identifier"=? AND "context"=? AND "cache"=?' . $this->getNotExpiredStatement());
137 $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier));
138 return $statementHandle->fetchColumn();
139 }
140
141 /**
142 * Checks if a cache entry with the specified identifier exists.
143 *
144 * @param string $entryIdentifier An identifier specifying the cache entry
145 * @return boolean TRUE if such an entry exists, FALSE if not
146 * @api
147 */
148 public function has($entryIdentifier) {
149 $statementHandle = $this->databaseHandle->prepare('SELECT COUNT("identifier") FROM "cache" WHERE "identifier"=? AND "context"=? AND "cache"=?' . $this->getNotExpiredStatement());
150 $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier));
151 return $statementHandle->fetchColumn() > 0;
152 }
153
154 /**
155 * Removes all cache entries matching the specified identifier.
156 * Usually this only affects one entry but if - for what reason ever -
157 * old entries for the identifier still exist, they are removed as well.
158 *
159 * @param string $entryIdentifier Specifies the cache entry to remove
160 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
161 * @api
162 */
163 public function remove($entryIdentifier) {
164 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "tags" WHERE "identifier"=? AND "context"=? AND "cache"=?');
165 $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier));
166 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "cache" WHERE "identifier"=? AND "context"=? AND "cache"=?');
167 $statementHandle->execute(array($entryIdentifier, $this->context, $this->cacheIdentifier));
168 return $statementHandle->rowCount() > 0;
169 }
170
171 /**
172 * Removes all cache entries of this cache.
173 *
174 * @return void
175 * @api
176 */
177 public function flush() {
178 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "tags" WHERE "context"=? AND "cache"=?');
179 $statementHandle->execute(array($this->context, $this->cacheIdentifier));
180 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "cache" WHERE "context"=? AND "cache"=?');
181 $statementHandle->execute(array($this->context, $this->cacheIdentifier));
182 }
183
184 /**
185 * Removes all cache entries of this cache which are tagged by the specified tag.
186 *
187 * @param string $tag The tag the entries must have
188 * @return void
189 * @api
190 */
191 public function flushByTag($tag) {
192 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "cache" WHERE "context"=? AND "cache"=? AND "identifier" IN (SELECT "identifier" FROM "tags" WHERE "context"=? AND "cache"=? AND "tag"=?)');
193 $statementHandle->execute(array($this->context, $this->cacheIdentifier, $this->context, $this->cacheIdentifier, $tag));
194 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "tags" WHERE "context"=? AND "cache"=? AND "tag"=?');
195 $statementHandle->execute(array($this->context, $this->cacheIdentifier, $tag));
196 }
197
198 /**
199 * Finds and returns all cache entry identifiers which are tagged by the
200 * specified tag.
201 *
202 * @param string $tag The tag to search for
203 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
204 * @api
205 */
206 public function findIdentifiersByTag($tag) {
207 $statementHandle = $this->databaseHandle->prepare('SELECT "identifier" FROM "tags" WHERE "context"=? AND "cache"=? AND "tag"=?');
208 $statementHandle->execute(array($this->context, $this->cacheIdentifier, $tag));
209 return $statementHandle->fetchAll(\PDO::FETCH_COLUMN);
210 }
211
212 /**
213 * Does garbage collection
214 *
215 * @return void
216 * @api
217 */
218 public function collectGarbage() {
219 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "tags" WHERE "context"=? AND "cache"=? AND "identifier" IN ' . '(SELECT "identifier" FROM "cache" WHERE "context"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME'] . ')');
220 $statementHandle->execute(array($this->context, $this->cacheIdentifier, $this->context, $this->cacheIdentifier));
221 $statementHandle = $this->databaseHandle->prepare('DELETE FROM "cache" WHERE "context"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME']);
222 $statementHandle->execute(array($this->context, $this->cacheIdentifier));
223 }
224
225 /**
226 * Returns an SQL statement that evaluates to TRUE if the entry is not expired.
227 *
228 * @return string
229 */
230 protected function getNotExpiredStatement() {
231 return ' AND ("lifetime" = 0 OR "created" + "lifetime" >= ' . $GLOBALS['EXEC_TIME'] . ')';
232 }
233
234 /**
235 * Connect to the database
236 *
237 * @return void
238 * @throws \RuntimeException if something goes wrong
239 */
240 protected function connect() {
241 try {
242 $splitdsn = explode(':', $this->dataSourceName, 2);
243 $this->pdoDriver = $splitdsn[0];
244 if ($this->pdoDriver === 'sqlite' && !file_exists($splitdsn[1])) {
245 $this->databaseHandle = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('PDO', $this->dataSourceName, $this->username, $this->password);
246 $this->createCacheTables();
247 } else {
248 $this->databaseHandle = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('PDO', $this->dataSourceName, $this->username, $this->password);
249 }
250 $this->databaseHandle->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
251 if (substr($this->pdoDriver, 0, 5) === 'mysql') {
252 $this->databaseHandle->exec('SET SESSION sql_mode=\'ANSI\';');
253 }
254 } catch (\PDOException $e) {
255 throw new \RuntimeException('Could not connect to cache table with DSN "' . $this->dataSourceName . '". PDO error: ' . $e->getMessage(), 1334736164);
256 }
257 }
258
259 /**
260 * Creates the tables needed for the cache backend.
261 *
262 * @return void
263 * @throws \RuntimeException if something goes wrong
264 */
265 protected function createCacheTables() {
266 try {
267 \TYPO3\CMS\Core\Database\PdoHelper::importSql(
268 $this->databaseHandle,
269 $this->pdoDriver,
270 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('core') .
271 'Resources/Private/Sql/Cache/Backend/PdoBackendCacheAndTags.sql'
272 );
273 } catch (\PDOException $e) {
274 throw new \RuntimeException('Could not create cache tables with DSN "' . $this->dataSourceName . '". PDO error: ' . $e->getMessage(), 1259576985);
275 }
276 }
277
278 }