TYPO3 CMS  TYPO3_7-6
Typo3DatabaseBackend.php
Go to the documentation of this file.
1 <?php
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  */
19 
25 {
29  const FAKED_UNLIMITED_EXPIRE = 2145909600;
33  protected $cacheTable;
34 
38  protected $tagsTable;
39 
43  protected $compression = false;
44 
48  protected $compressionLevel = -1;
49 
53  protected $identifierField;
54 
58  protected $expiresField;
59 
63  protected $maximumLifetime;
64 
69 
73  protected $expiredStatement;
74 
78  protected $tableList;
79 
83  protected $tableJoin;
84 
93  {
94  parent::setCache($cache);
95  $this->cacheTable = 'cf_' . $this->cacheIdentifier;
96  $this->tagsTable = 'cf_' . $this->cacheIdentifier . '_tags';
98  }
99 
105  protected function initializeCommonReferences()
106  {
107  $this->identifierField = $this->cacheTable . '.identifier';
108  $this->expiresField = $this->cacheTable . '.expires';
109  $this->maximumLifetime = self::FAKED_UNLIMITED_EXPIRE - $GLOBALS['EXEC_TIME'];
110  $this->tableList = $this->cacheTable . ', ' . $this->tagsTable;
111  $this->tableJoin = $this->identifierField . ' = ' . $this->tagsTable . '.identifier';
112  $this->expiredStatement = $this->expiresField . ' < ' . $GLOBALS['EXEC_TIME'];
113  $this->notExpiredStatement = $this->expiresField . ' >= ' . $GLOBALS['EXEC_TIME'];
114  }
115 
127  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
128  {
130  if (!is_string($data)) {
131  throw new Exception\InvalidDataException(
132  'The specified data is of type "' . gettype($data) . '" but a string is expected.',
133  1236518298
134  );
135  }
136  if (is_null($lifetime)) {
137  $lifetime = $this->defaultLifetime;
138  }
139  if ($lifetime === 0 || $lifetime > $this->maximumLifetime) {
140  $lifetime = $this->maximumLifetime;
141  }
142  $expires = $GLOBALS['EXEC_TIME'] + $lifetime;
143  $this->remove($entryIdentifier);
144  if ($this->compression) {
145  $data = gzcompress($data, $this->compressionLevel);
146  }
147  $GLOBALS['TYPO3_DB']->exec_INSERTquery($this->cacheTable, [
148  'identifier' => $entryIdentifier,
149  'expires' => $expires,
150  'content' => $data
151  ]);
152  if (!empty($tags)) {
153  $fields = [];
154  $fields[] = 'identifier';
155  $fields[] = 'tag';
156  $tagRows = [];
157  foreach ($tags as $tag) {
158  $tagRow = [];
159  $tagRow[] = $entryIdentifier;
160  $tagRow[] = $tag;
161  $tagRows[] = $tagRow;
162  }
163  $GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows($this->tagsTable, $fields, $tagRows);
164  }
165  }
166 
173  public function get($entryIdentifier)
174  {
176 
177  $cacheEntry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
178  'content',
179  $this->cacheTable,
180  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' AND ' . $this->notExpiredStatement
181  );
182  if (is_array($cacheEntry)) {
183  $cacheEntry = $cacheEntry['content'];
184  }
185  if ($this->compression && (string)$cacheEntry !== '') {
186  $cacheEntry = gzuncompress($cacheEntry);
187  }
188  return $cacheEntry !== null ? $cacheEntry : false;
189  }
190 
197  public function has($entryIdentifier)
198  {
200  $hasEntry = false;
201  $cacheEntries = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
202  '*',
203  $this->cacheTable,
204  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' AND ' . $this->notExpiredStatement
205  );
206  if ($cacheEntries >= 1) {
207  $hasEntry = true;
208  }
209  return $hasEntry;
210  }
211 
219  public function remove($entryIdentifier)
220  {
222  $entryRemoved = false;
223  $GLOBALS['TYPO3_DB']->exec_DELETEquery(
224  $this->cacheTable,
225  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable)
226  );
227  // we need to save the affected rows as mysqli_affected_rows just returns the amount of affected rows
228  // of the last call
229  $affectedRows = $GLOBALS['TYPO3_DB']->sql_affected_rows();
230  $GLOBALS['TYPO3_DB']->exec_DELETEquery(
231  $this->tagsTable,
232  'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->tagsTable)
233  );
234  if ($affectedRows == 1) {
235  $entryRemoved = true;
236  }
237  return $entryRemoved;
238  }
239 
246  public function findIdentifiersByTag($tag)
247  {
249  $cacheEntryIdentifiers = [];
250  $cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
251  $this->identifierField,
252  $this->tableList,
253  $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable) . ' AND ' . $this->tableJoin . ' AND ' . $this->notExpiredStatement,
254  $this->identifierField
255  );
256  foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
257  $cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
258  }
259  return $cacheEntryIdentifiers;
260  }
261 
267  public function flush()
268  {
270  $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->cacheTable);
271  $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->tagsTable);
272  }
273 
280  public function flushByTag($tag)
281  {
283 
284  if ($this->isConnectionMysql()) {
285  $GLOBALS['TYPO3_DB']->sql_query('
286  DELETE tags2, cache1'
287  . ' FROM ' . $this->tagsTable . ' AS tags1'
288  . ' JOIN ' . $this->tagsTable . ' AS tags2 ON tags1.identifier = tags2.identifier'
289  . ' JOIN ' . $this->cacheTable . ' AS cache1 ON tags1.identifier = cache1.identifier'
290  . ' WHERE tags1.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable)
291  );
292  } else {
293  $tagsTableWhereClause = $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable);
294  $cacheEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT identifier', $this->tagsTable, $tagsTableWhereClause);
295  $cacheEntryIdentifiers = [];
296  while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsResource)) {
297  $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($cacheEntryIdentifierRow['identifier'], $this->cacheTable);
298  }
299  $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsResource);
300  if (!empty($cacheEntryIdentifiers)) {
301  $deleteWhereClause = 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')';
302  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->cacheTable, $deleteWhereClause);
303  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->tagsTable, $deleteWhereClause);
304  }
305  }
306  }
307 
313  public function collectGarbage()
314  {
316 
317  if ($this->isConnectionMysql()) {
318  // First delete all expired rows from cache table and their connected tag rows
319  $GLOBALS['TYPO3_DB']->sql_query(
320  'DELETE cache, tags'
321  . ' FROM ' . $this->cacheTable . ' AS cache'
322  . ' LEFT OUTER JOIN ' . $this->tagsTable . ' AS tags ON cache.identifier = tags.identifier'
323  . ' WHERE cache.expires < ' . $GLOBALS['EXEC_TIME']
324  );
325  // Then delete possible "orphaned" rows from tags table - tags that have no cache row for whatever reason
326  $GLOBALS['TYPO3_DB']->sql_query(
327  'DELETE tags'
328  . ' FROM ' . $this->tagsTable . ' AS tags'
329  . ' LEFT OUTER JOIN ' . $this->cacheTable . ' AS cache ON tags.identifier = cache.identifier'
330  . ' WHERE cache.identifier IS NULL'
331  );
332  } else {
333  // Get identifiers of expired cache entries
334  $cacheEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT identifier', $this->cacheTable, 'expires < ' . $GLOBALS['EXEC_TIME']);
335  $cacheEntryIdentifiers = [];
336  while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsResource)) {
337  $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($cacheEntryIdentifierRow['identifier'], $this->tagsTable);
338  }
339  $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsResource);
340  // Delete tag rows connected to expired cache entries
341  if (!empty($cacheEntryIdentifiers)) {
342  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->tagsTable, 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')');
343  }
344  // Delete expired cache rows
345  $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->cacheTable, 'expires < ' . $GLOBALS['EXEC_TIME']);
346 
347  // Find out which "orphaned" tags rows exists that have no cache row and delete those, too.
348  $result = $GLOBALS['TYPO3_DB']->sql_query(
349  'SELECT tags.identifier'
350  . ' FROM ' . $this->tagsTable . ' AS tags'
351  . ' LEFT OUTER JOIN ' . $this->cacheTable . ' AS cache ON tags.identifier = cache.identifier'
352  . ' WHERE cache.identifier IS NULL'
353  . ' GROUP BY tags.identifier'
354  );
355 
356  while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
357  $tagsEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($row['identifier'], $this->tagsTable);
358  }
359 
360  if (!empty($tagsEntryIdentifiers)) {
361  $GLOBALS['TYPO3_DB']->sql_query(
362  'DELETE'
363  . ' FROM ' . $this->tagsTable
364  . ' WHERE identifier IN (' . implode(',', $tagsEntryIdentifiers) . ')'
365  );
366  }
367  }
368  }
369 
375  public function getCacheTable()
376  {
378  return $this->cacheTable;
379  }
380 
386  public function getTagsTable()
387  {
389  return $this->tagsTable;
390  }
391 
397  public function setCompression($compression)
398  {
399  $this->compression = $compression;
400  }
401 
410  {
411  if ($compressionLevel >= -1 && $compressionLevel <= 9) {
412  $this->compressionLevel = $compressionLevel;
413  }
414  }
415 
423  {
424  if (!$this->cache instanceof FrontendInterface) {
425  throw new Exception('No cache frontend has been set via setCache() yet.', 1236518288);
426  }
427  }
428 
436  public function getTableDefinitions()
437  {
438  $cacheTableSql = file_get_contents(
440  'Resources/Private/Sql/Cache/Backend/Typo3DatabaseBackendCache.sql'
441  );
442  $requiredTableStructures = str_replace('###CACHE_TABLE###', $this->cacheTable, $cacheTableSql) . LF . LF;
443  $tagsTableSql = file_get_contents(
445  'Resources/Private/Sql/Cache/Backend/Typo3DatabaseBackendTags.sql'
446  );
447  $requiredTableStructures .= str_replace('###TAGS_TABLE###', $this->tagsTable, $tagsTableSql) . LF;
448  return $requiredTableStructures;
449  }
450 
457  protected function isConnectionMysql()
458  {
459  return !((bool)ExtensionManagementUtility::isLoaded('dbal'));
460  }
461 }
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']