TYPO3  7.6
CacheManager.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Cache;
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 use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException;
18 
27 {
31  protected $cacheFactory;
32 
36  protected $caches = array();
37 
41  protected $cacheConfigurations = array();
42 
51  protected $cacheGroups = array();
52 
56  protected $defaultCacheConfiguration = array(
57  'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
58  'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class,
59  'options' => array(),
60  'groups' => array('all')
61  );
62 
67  public function injectCacheFactory(\TYPO3\CMS\Core\Cache\CacheFactory $cacheFactory)
68  {
69  $this->cacheFactory = $cacheFactory;
70  }
71 
89  {
90  foreach ($cacheConfigurations as $identifier => $configuration) {
91  if (!is_array($configuration)) {
92  throw new \InvalidArgumentException('The cache configuration for cache "' . $identifier . '" was not an array as expected.', 1231259656);
93  }
94  $this->cacheConfigurations[$identifier] = $configuration;
95  }
96  }
97 
106  public function registerCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
107  {
108  $identifier = $cache->getIdentifier();
109  if (isset($this->caches[$identifier])) {
110  throw new \TYPO3\CMS\Core\Cache\Exception\DuplicateIdentifierException('A cache with identifier "' . $identifier . '" has already been registered.', 1203698223);
111  }
112  $this->caches[$identifier] = $cache;
113  }
114 
123  public function getCache($identifier)
124  {
125  if ($this->hasCache($identifier) === false) {
126  throw new \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException('A cache with identifier "' . $identifier . '" does not exist.', 1203699034);
127  }
128  if (!isset($this->caches[$identifier])) {
129  $this->createCache($identifier);
130  }
131  return $this->caches[$identifier];
132  }
133 
141  public function hasCache($identifier)
142  {
143  return isset($this->caches[$identifier]) || isset($this->cacheConfigurations[$identifier]);
144  }
145 
152  public function flushCaches()
153  {
154  $this->createAllCaches();
155  foreach ($this->caches as $cache) {
156  $cache->flush();
157  }
158  }
159 
168  public function flushCachesInGroup($groupIdentifier)
169  {
170  $this->createAllCaches();
171  if (isset($this->cacheGroups[$groupIdentifier])) {
172  foreach ($this->cacheGroups[$groupIdentifier] as $cacheIdentifier) {
173  if (isset($this->caches[$cacheIdentifier])) {
174  $this->caches[$cacheIdentifier]->flush();
175  }
176  }
177  } else {
178  throw new NoSuchCacheGroupException('No cache in the specified group \'' . $groupIdentifier . '\'', 1390334120);
179  }
180  }
181 
192  public function flushCachesInGroupByTag($groupIdentifier, $tag)
193  {
194  $this->createAllCaches();
195  if (isset($this->cacheGroups[$groupIdentifier])) {
196  foreach ($this->cacheGroups[$groupIdentifier] as $cacheIdentifier) {
197  if (isset($this->caches[$cacheIdentifier])) {
198  $this->caches[$cacheIdentifier]->flushByTag($tag);
199  }
200  }
201  } else {
202  throw new NoSuchCacheGroupException('No cache in the specified group \'' . $groupIdentifier . '\'', 1390337129);
203  }
204  }
205 
206 
215  public function flushCachesByTag($tag)
216  {
217  $this->createAllCaches();
218  foreach ($this->caches as $cache) {
219  $cache->flushByTag($tag);
220  }
221  }
222 
241  public function flushClassFileCachesByChangedFiles($fileMonitorIdentifier, array $changedFiles)
242  {
243  $modifiedClassNamesWithUnderscores = array();
244  $objectClassesCache = $this->getCache('FLOW3_Object_Classes');
245  $objectConfigurationCache = $this->getCache('FLOW3_Object_Configuration');
246  switch ($fileMonitorIdentifier) {
247  case 'FLOW3_ClassFiles':
248  $modifiedAspectClassNamesWithUnderscores = array();
249  foreach ($changedFiles as $pathAndFilename => $status) {
250  $pathAndFilename = str_replace(FLOW3_PATH_PACKAGES, '', $pathAndFilename);
251  $matches = array();
252  if (preg_match('/[^\\/]+\\/(.+)\\/(Classes|Tests)\\/(.+)\\.php/', $pathAndFilename, $matches) === 1) {
253  $classNameWithUnderscores = str_replace(array('/', '.'), '_', $matches[1] . '_' . ($matches[2] === 'Tests' ? 'Tests_' : '') . $matches[3]);
254  $modifiedClassNamesWithUnderscores[$classNameWithUnderscores] = true;
255  // If an aspect was modified, the whole code cache needs to be flushed, so keep track of them:
256  if (substr($classNameWithUnderscores, -6, 6) === 'Aspect') {
257  $modifiedAspectClassNamesWithUnderscores[$classNameWithUnderscores] = true;
258  }
259  // As long as no modified aspect was found, we are optimistic that only part of the cache needs to be flushed:
260  if (empty($modifiedAspectClassNamesWithUnderscores)) {
261  $objectClassesCache->remove($classNameWithUnderscores);
262  }
263  }
264  }
265  $flushDoctrineProxyCache = false;
266  if (!empty($modifiedClassNamesWithUnderscores)) {
267  $reflectionStatusCache = $this->getCache('FLOW3_Reflection_Status');
268  foreach ($modifiedClassNamesWithUnderscores as $classNameWithUnderscores => $_) {
269  $reflectionStatusCache->remove($classNameWithUnderscores);
270  if ($flushDoctrineProxyCache === false && preg_match('/_Domain_Model_(.+)/', $classNameWithUnderscores) === 1) {
271  $flushDoctrineProxyCache = true;
272  }
273  }
274  $objectConfigurationCache->remove('allCompiledCodeUpToDate');
275  }
276  if (!empty($modifiedAspectClassNamesWithUnderscores)) {
277  $this->systemLogger->log('Aspect classes have been modified, flushing the whole proxy classes cache.', LOG_INFO);
278  $objectClassesCache->flush();
279  }
280  if ($flushDoctrineProxyCache === true) {
281  $this->systemLogger->log('Domain model changes have been detected, triggering Doctrine 2 proxy rebuilding.', LOG_INFO);
282  $objectConfigurationCache->remove('doctrineProxyCodeUpToDate');
283  }
284  break;
285  case 'FLOW3_ConfigurationFiles':
286  $policyChangeDetected = false;
287  $routesChangeDetected = false;
288  foreach ($changedFiles as $pathAndFilename => $_) {
289  $filename = basename($pathAndFilename);
290  if (!in_array($filename, array('Policy.yaml', 'Routes.yaml'))) {
291  continue;
292  }
293  if ($policyChangeDetected === false && $filename === 'Policy.yaml') {
294  $this->systemLogger->log('The security policies have changed, flushing the policy cache.', LOG_INFO);
295  $this->getCache('FLOW3_Security_Policy')->flush();
296  $policyChangeDetected = true;
297  } elseif ($routesChangeDetected === false && $filename === 'Routes.yaml') {
298  $this->systemLogger->log('A Routes.yaml file has been changed, flushing the routing cache.', LOG_INFO);
299  $this->getCache('FLOW3_Mvc_Routing_FindMatchResults')->flush();
300  $this->getCache('FLOW3_Mvc_Routing_Resolve')->flush();
301  $routesChangeDetected = true;
302  }
303  }
304  $this->systemLogger->log('The configuration has changed, triggering an AOP proxy class rebuild.', LOG_INFO);
305  $objectConfigurationCache->remove('allAspectClassesUpToDate');
306  $objectConfigurationCache->remove('allCompiledCodeUpToDate');
307  $objectClassesCache->flush();
308  break;
309  case 'FLOW3_TranslationFiles':
310  foreach ($changedFiles as $pathAndFilename => $status) {
311  $matches = array();
312  if (preg_match('/\\/Translations\\/.+\\.xlf/', $pathAndFilename, $matches) === 1) {
313  $this->systemLogger->log('The localization files have changed, thus flushing the I18n XML model cache.', LOG_INFO);
314  $this->getCache('FLOW3_I18n_XmlModelCache')->flush();
315  break;
316  }
317  }
318  break;
319  }
320  }
321 
337  public static function getClassTag($className = '')
338  {
339  return $className === '' ? \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::TAG_CLASS : \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::TAG_CLASS . str_replace('\\', '_', $className);
340  }
341 
347  protected function createAllCaches()
348  {
349  foreach ($this->cacheConfigurations as $identifier => $_) {
350  if (!isset($this->caches[$identifier])) {
351  $this->createCache($identifier);
352  }
353  }
354  }
355 
362  protected function createCache($identifier)
363  {
364  if (isset($this->cacheConfigurations[$identifier]['frontend'])) {
365  $frontend = $this->cacheConfigurations[$identifier]['frontend'];
366  } else {
367  $frontend = $this->defaultCacheConfiguration['frontend'];
368  }
369  if (isset($this->cacheConfigurations[$identifier]['backend'])) {
370  $backend = $this->cacheConfigurations[$identifier]['backend'];
371  } else {
372  $backend = $this->defaultCacheConfiguration['backend'];
373  }
374  if (isset($this->cacheConfigurations[$identifier]['options'])) {
375  $backendOptions = $this->cacheConfigurations[$identifier]['options'];
376  } else {
377  $backendOptions = $this->defaultCacheConfiguration['options'];
378  }
379 
380  // Add the cache identifier to the groups that it should be attached to, or use the default ones.
381  if (isset($this->cacheConfigurations[$identifier]['groups']) && is_array($this->cacheConfigurations[$identifier]['groups'])) {
382  $assignedGroups = $this->cacheConfigurations[$identifier]['groups'];
383  } else {
384  $assignedGroups = $this->defaultCacheConfiguration['groups'];
385  }
386  foreach ($assignedGroups as $groupIdentifier) {
387  if (!isset($this->cacheGroups[$groupIdentifier])) {
388  $this->cacheGroups[$groupIdentifier] = array();
389  }
390  $this->cacheGroups[$groupIdentifier][] = $identifier;
391  }
392 
393  $this->cacheFactory->create($identifier, $frontend, $backend, $backendOptions);
394  }
395 }