TYPO3  7.6
Typo3DatabaseBackend.php
Go to the documentation of this file.
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 
22 {
26  const FAKED_UNLIMITED_EXPIRE = 2145909600;
30  protected $cacheTable;
31 
35  protected $tagsTable;
36 
40  protected $compression = false;
41 
45  protected $compressionLevel = -1;
46 
50  protected $identifierField;
51 
55  protected $expiresField;
56 
60  protected $maximumLifetime;
61 
66 
70  protected $expiredStatement;
71 
75  protected $tableList;
76 
80  protected $tableJoin;
81 
89  public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
90  {
91  parent::setCache($cache);
92  $this->cacheTable = 'cf_' . $this->cacheIdentifier;
93  $this->tagsTable = 'cf_' . $this->cacheIdentifier . '_tags';
95  }
96 
102  protected function initializeCommonReferences()
103  {
104  $this->identifierField = $this->cacheTable . '.identifier';
105  $this->expiresField = $this->cacheTable . '.expires';
106  $this->maximumLifetime = self::FAKED_UNLIMITED_EXPIRE - $GLOBALS['EXEC_TIME'];
107  $this->tableList = $this->cacheTable . ', ' . $this->tagsTable;
108  $this->tableJoin = $this->identifierField . ' = ' . $this->tagsTable . '.identifier';
109  $this->expiredStatement = $this->expiresField . ' < ' . $GLOBALS['EXEC_TIME'];
110  $this->notExpiredStatement = $this->expiresField . ' >= ' . $GLOBALS['EXEC_TIME'];
111  }
112 
124  public function set($entryIdentifier, $data, array $tags = array(), $lifetime = null)
125  {
127  if (!is_string($data)) {
128  throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException(
129  'The specified data is of type "' . gettype($data) . '" but a string is expected.',
130  1236518298
131  );
132  }
133  if (is_null($lifetime)) {
134  $lifetime = $this->defaultLifetime;
135  }
136  if ($lifetime === 0 || $lifetime > $this->maximumLifetime) {
137  $lifetime = $this->maximumLifetime;
138  }
139  $expires = $GLOBALS['EXEC_TIME'] + $lifetime;
140  $this->remove($entryIdentifier);
141  if ($this->compression) {
142  $data = gzcompress($data, $this->compressionLevel);
143  }
144  $GLOBALS['TYPO3_DB']->exec_INSERTquery($this->cacheTable, array(
145  'identifier' => $entryIdentifier,
146  'expires' => $expires,
147  'content' => $data
148  ));
149  if (!empty($tags)) {
150  $fields = array();
151  $fields[] = 'identifier';
152  $fields[] = 'tag';
153  $tagRows = array();
154  foreach ($tags as $tag) {
155  $tagRow = array();
156  $tagRow[] = $entryIdentifier;
157  $tagRow[] = $tag;
158  $tagRows[] = $tagRow;
159  }
160  $GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows($this->tagsTable, $fields, $tagRows);
161  }
162  }
163 
170  public function get($entryIdentifier)
171  {
173 
174  $cacheEntry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
175  'content',
176  $this->cacheTable,
177  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' AND ' . $this->notExpiredStatement
178  );
179  if (is_array($cacheEntry)) {
180  $cacheEntry = $cacheEntry['content'];
181  }
182  if ($this->compression && (string)$cacheEntry !== '') {
183  $cacheEntry = gzuncompress($cacheEntry);
184  }
185  return $cacheEntry !== null ? $cacheEntry : false;
186  }
187 
194  public function has($entryIdentifier)
195  {
197  $hasEntry = false;
198  $cacheEntries = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
199  '*',
200  $this->cacheTable,
201  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' AND ' . $this->notExpiredStatement
202  );
203  if ($cacheEntries >= 1) {
204  $hasEntry = true;
205  }
206  return $hasEntry;
207  }
208 
216  public function remove($entryIdentifier)
217  {
219  $entryRemoved = false;
220  $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
221  $this->cacheTable,
222  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable)
223  );
224  $GLOBALS['TYPO3_DB']->exec_DELETEquery(
225  $this->tagsTable,
226  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->tagsTable)
227  );
228  if ($GLOBALS['TYPO3_DB']->sql_affected_rows($res) == 1) {
229  $entryRemoved = true;
230  }
231  return $entryRemoved;
232  }
233 
240  public function findIdentifiersByTag($tag)
241  {
243  $cacheEntryIdentifiers = array();
244  $cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
245  $this->identifierField,
246  $this->tableList,
247  $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable) . ' AND ' . $this->tableJoin . ' AND ' . $this->notExpiredStatement,
248  $this->identifierField
249  );
250  foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
251  $cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
252  }
253  return $cacheEntryIdentifiers;
254  }
255 
261  public function flush()
262  {
264  $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->cacheTable);
265  $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->tagsTable);
266  }
267 
274  public function flushByTag($tag)
275  {
277 
278  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal')) {
279  $this->flushByTagDbal($tag);
280  } else {
281  $GLOBALS['TYPO3_DB']->sql_query('
282  DELETE ' . $this->cacheTable . ', ' . $this->tagsTable . '
283  FROM ' . $this->cacheTable . ' JOIN ' . $this->tagsTable . ' ON ' . $this->cacheTable . '.identifier=' . $this->tagsTable . '.identifier
284  WHERE ' . $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable)
285  );
286  }
287  }
288 
295  protected function flushByTagDbal($tag)
296  {
297  $tagsTableWhereClause = $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable);
298  $cacheEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT identifier', $this->tagsTable, $tagsTableWhereClause);
299  $cacheEntryIdentifiers = array();
300  while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsResource)) {
301  $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($cacheEntryIdentifierRow['identifier'], $this->cacheTable);
302  }
303  $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsResource);
304  if (!empty($cacheEntryIdentifiers)) {
305  $deleteWhereClause = 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')';
306  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->cacheTable, $deleteWhereClause);
307  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->tagsTable, $deleteWhereClause);
308  }
309  }
310 
316  public function collectGarbage()
317  {
319 
320  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal')) {
321  $this->collectGarbageDbal();
322  } else {
323  $GLOBALS['TYPO3_DB']->sql_query('
324  DELETE ' . $this->cacheTable . ', ' . $this->tagsTable . '
325  FROM ' . $this->cacheTable . ' JOIN ' . $this->tagsTable . ' ON ' . $this->cacheTable . '.identifier=' . $this->tagsTable . '.identifier
326  WHERE ' . $this->expiredStatement
327  );
328  }
329  }
330 
336  protected function collectGarbageDbal()
337  {
338  // Get identifiers of expired cache entries
339  $cacheEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT identifier', $this->cacheTable, $this->expiredStatement);
340  $cacheEntryIdentifiers = array();
341  while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsResource)) {
342  $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($cacheEntryIdentifierRow['identifier'], $this->tagsTable);
343  }
344  $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsResource);
345  // Delete tag rows connected to expired cache entries
346  if (!empty($cacheEntryIdentifiers)) {
347  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->tagsTable, 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')');
348  }
349  // Delete expired cache rows
350  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->cacheTable, $this->expiredStatement);
351  }
352 
358  public function getCacheTable()
359  {
361  return $this->cacheTable;
362  }
363 
369  public function getTagsTable()
370  {
372  return $this->tagsTable;
373  }
374 
380  public function setCompression($compression)
381  {
382  $this->compression = $compression;
383  }
384 
393  {
394  if ($compressionLevel >= -1 && $compressionLevel <= 9) {
395  $this->compressionLevel = $compressionLevel;
396  }
397  }
398 
406  {
407  if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
408  throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set via setCache() yet.', 1236518288);
409  }
410  }
411 
419  public function getTableDefinitions()
420  {
421  $cacheTableSql = file_get_contents(
422  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('core') .
423  'Resources/Private/Sql/Cache/Backend/Typo3DatabaseBackendCache.sql'
424  );
425  $requiredTableStructures = str_replace('###CACHE_TABLE###', $this->cacheTable, $cacheTableSql) . LF . LF;
426  $tagsTableSql = file_get_contents(
427  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('core') .
428  'Resources/Private/Sql/Cache/Backend/Typo3DatabaseBackendTags.sql'
429  );
430  $requiredTableStructures .= str_replace('###TAGS_TABLE###', $this->tagsTable, $tagsTableSql) . LF;
431  return $requiredTableStructures;
432  }
433 }