[TASK] Use arrays in str_replace() calls
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Cache / CacheManager.php
1 <?php
2 namespace TYPO3\CMS\Core\Cache;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2009-2013 Ingo Renner <ingo@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /**
27 * The Cache Manager
28 *
29 * This file is a backport from FLOW3
30 *
31 * @author Robert Lemke <robert@typo3.org>
32 * @scope singleton
33 * @api
34 */
35 class CacheManager implements \TYPO3\CMS\Core\SingletonInterface {
36
37 /**
38 * @var \TYPO3\CMS\Core\Cache\CacheFactory
39 */
40 protected $cacheFactory;
41
42 /**
43 * @var array
44 */
45 protected $caches = array();
46
47 /**
48 * @var array
49 */
50 protected $cacheConfigurations = array();
51
52 /**
53 * @var array Default cache configuration as fallback
54 */
55 protected $defaultCacheConfiguration = array(
56 'frontend' => 'TYPO3\\CMS\\Core\\Cache\\Frontend\\VariableFrontend',
57 'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
58 'options' => array()
59 );
60
61 /**
62 * @param \TYPO3\CMS\Core\Cache\CacheFactory $cacheFactory
63 * @return void
64 */
65 public function injectCacheFactory(\TYPO3\CMS\Core\Cache\CacheFactory $cacheFactory) {
66 $this->cacheFactory = $cacheFactory;
67 }
68
69 /**
70 * Sets configurations for caches. The key of each entry specifies the
71 * cache identifier and the value is an array of configuration options.
72 * Possible options are:
73 *
74 * frontend
75 * backend
76 * backendOptions
77 *
78 * If one of the options is not specified, the default value is assumed.
79 * Existing cache configurations are preserved.
80 *
81 * @param array $cacheConfigurations The cache configurations to set
82 * @return void
83 * @throws \InvalidArgumentException If $cacheConfigurations is not an array
84 */
85 public function setCacheConfigurations(array $cacheConfigurations) {
86 foreach ($cacheConfigurations as $identifier => $configuration) {
87 if (!is_array($configuration)) {
88 throw new \InvalidArgumentException('The cache configuration for cache "' . $identifier . '" was not an array as expected.', 1231259656);
89 }
90 $this->cacheConfigurations[$identifier] = $configuration;
91 }
92 }
93
94 /**
95 * Registers a cache so it can be retrieved at a later point.
96 *
97 * @param \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache The cache frontend to be registered
98 * @return void
99 * @throws \TYPO3\CMS\Core\Cache\Exception\DuplicateIdentifierException if a cache with the given identifier has already been registered.
100 * @api
101 */
102 public function registerCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache) {
103 $identifier = $cache->getIdentifier();
104 if (isset($this->caches[$identifier])) {
105 throw new \TYPO3\CMS\Core\Cache\Exception\DuplicateIdentifierException('A cache with identifier "' . $identifier . '" has already been registered.', 1203698223);
106 }
107 $this->caches[$identifier] = $cache;
108 }
109
110 /**
111 * Returns the cache specified by $identifier
112 *
113 * @param string $identifier Identifies which cache to return
114 * @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface The specified cache frontend
115 * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
116 * @api
117 */
118 public function getCache($identifier) {
119 if ($this->hasCache($identifier) === FALSE) {
120 throw new \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException('A cache with identifier "' . $identifier . '" does not exist.', 1203699034);
121 }
122 if (!isset($this->caches[$identifier])) {
123 $this->createCache($identifier);
124 }
125 return $this->caches[$identifier];
126 }
127
128 /**
129 * Checks if the specified cache has been registered.
130 *
131 * @param string $identifier The identifier of the cache
132 * @return boolean TRUE if a cache with the given identifier exists, otherwise FALSE
133 * @api
134 */
135 public function hasCache($identifier) {
136 return isset($this->caches[$identifier]) || isset($this->cacheConfigurations[$identifier]);
137 }
138
139 /**
140 * Flushes all registered caches
141 *
142 * @return void
143 * @api
144 */
145 public function flushCaches() {
146 $this->createAllCaches();
147 foreach ($this->caches as $cache) {
148 $cache->flush();
149 }
150 }
151
152 /**
153 * Flushes entries tagged by the specified tag of all registered
154 * caches.
155 *
156 * @param string $tag Tag to search for
157 * @return void
158 * @api
159 */
160 public function flushCachesByTag($tag) {
161 $this->createAllCaches();
162 foreach ($this->caches as $cache) {
163 $cache->flushByTag($tag);
164 }
165 }
166
167 /**
168 * TYPO3 v4 note: This method is a direct backport from FLOW3 and currently
169 * unused in TYPO3 v4 context.
170 *
171 * Flushes entries tagged with class names if their class source files have changed.
172 * Also flushes AOP proxy caches if a policy was modified.
173 *
174 * This method is used as a slot for a signal sent by the system file monitor
175 * defined in the bootstrap scripts.
176 *
177 * Note: Policy configuration handling is implemented here as well as other parts
178 * of FLOW3 (like the security framework) are not fully initialized at the
179 * time needed.
180 *
181 * @param string $fileMonitorIdentifier Identifier of the File Monitor
182 * @param array $changedFiles A list of full paths to changed files
183 * @return void
184 */
185 public function flushClassFileCachesByChangedFiles($fileMonitorIdentifier, array $changedFiles) {
186 $modifiedClassNamesWithUnderscores = array();
187 $objectClassesCache = $this->getCache('FLOW3_Object_Classes');
188 $objectConfigurationCache = $this->getCache('FLOW3_Object_Configuration');
189 switch ($fileMonitorIdentifier) {
190 case 'FLOW3_ClassFiles':
191 $modifiedAspectClassNamesWithUnderscores = array();
192 foreach ($changedFiles as $pathAndFilename => $status) {
193 $pathAndFilename = str_replace(FLOW3_PATH_PACKAGES, '', $pathAndFilename);
194 $matches = array();
195 if (preg_match('/[^\\/]+\\/(.+)\\/(Classes|Tests)\\/(.+)\\.php/', $pathAndFilename, $matches) === 1) {
196 $classNameWithUnderscores = str_replace(array('/', '.'), '_', $matches[1] . '_' . ($matches[2] === 'Tests' ? 'Tests_' : '') . $matches[3]);
197 $modifiedClassNamesWithUnderscores[$classNameWithUnderscores] = TRUE;
198 // If an aspect was modified, the whole code cache needs to be flushed, so keep track of them:
199 if (substr($classNameWithUnderscores, -6, 6) === 'Aspect') {
200 $modifiedAspectClassNamesWithUnderscores[$classNameWithUnderscores] = TRUE;
201 }
202 // As long as no modified aspect was found, we are optimistic that only part of the cache needs to be flushed:
203 if (count($modifiedAspectClassNamesWithUnderscores) === 0) {
204 $objectClassesCache->remove($classNameWithUnderscores);
205 }
206 }
207 }
208 $flushDoctrineProxyCache = FALSE;
209 if (count($modifiedClassNamesWithUnderscores) > 0) {
210 $reflectionStatusCache = $this->getCache('FLOW3_Reflection_Status');
211 foreach (array_keys($modifiedClassNamesWithUnderscores) as $classNameWithUnderscores) {
212 $reflectionStatusCache->remove($classNameWithUnderscores);
213 if ($flushDoctrineProxyCache === FALSE && preg_match('/_Domain_Model_(.+)/', $classNameWithUnderscores) === 1) {
214 $flushDoctrineProxyCache = TRUE;
215 }
216 }
217 $objectConfigurationCache->remove('allCompiledCodeUpToDate');
218 }
219 if (count($modifiedAspectClassNamesWithUnderscores) > 0) {
220 $this->systemLogger->log('Aspect classes have been modified, flushing the whole proxy classes cache.', LOG_INFO);
221 $objectClassesCache->flush();
222 }
223 if ($flushDoctrineProxyCache === TRUE) {
224 $this->systemLogger->log('Domain model changes have been detected, triggering Doctrine 2 proxy rebuilding.', LOG_INFO);
225 $objectConfigurationCache->remove('doctrineProxyCodeUpToDate');
226 }
227 break;
228 case 'FLOW3_ConfigurationFiles':
229 $policyChangeDetected = FALSE;
230 $routesChangeDetected = FALSE;
231 foreach (array_keys($changedFiles) as $pathAndFilename) {
232 $filename = basename($pathAndFilename);
233 if (!in_array($filename, array('Policy.yaml', 'Routes.yaml'))) {
234 continue;
235 }
236 if ($policyChangeDetected === FALSE && $filename === 'Policy.yaml') {
237 $this->systemLogger->log('The security policies have changed, flushing the policy cache.', LOG_INFO);
238 $this->getCache('FLOW3_Security_Policy')->flush();
239 $policyChangeDetected = TRUE;
240 } elseif ($routesChangeDetected === FALSE && $filename === 'Routes.yaml') {
241 $this->systemLogger->log('A Routes.yaml file has been changed, flushing the routing cache.', LOG_INFO);
242 $this->getCache('FLOW3_Mvc_Routing_FindMatchResults')->flush();
243 $this->getCache('FLOW3_Mvc_Routing_Resolve')->flush();
244 $routesChangeDetected = TRUE;
245 }
246 }
247 $this->systemLogger->log('The configuration has changed, triggering an AOP proxy class rebuild.', LOG_INFO);
248 $objectConfigurationCache->remove('allAspectClassesUpToDate');
249 $objectConfigurationCache->remove('allCompiledCodeUpToDate');
250 $objectClassesCache->flush();
251 break;
252 case 'FLOW3_TranslationFiles':
253 foreach ($changedFiles as $pathAndFilename => $status) {
254 $matches = array();
255 if (preg_match('/\\/Translations\\/.+\\.xlf/', $pathAndFilename, $matches) === 1) {
256 $this->systemLogger->log('The localization files have changed, thus flushing the I18n XML model cache.', LOG_INFO);
257 $this->getCache('FLOW3_I18n_XmlModelCache')->flush();
258 break;
259 }
260 }
261 break;
262 }
263 }
264
265 /**
266 * TYPO3 v4 note: This method is a direct backport from FLOW3 and currently
267 * unused in TYPO3 v4 context.
268 *
269 * Renders a tag which can be used to mark a cache entry as "depends on this class".
270 * Whenever the specified class is modified, all cache entries tagged with the
271 * class are flushed.
272 *
273 * If an empty string is specified as class name, the returned tag means
274 * "this cache entry becomes invalid if any of the known classes changes".
275 *
276 * @param string $className The class name
277 * @return string Class Tag
278 * @api
279 */
280 static public function getClassTag($className = '') {
281 return $className === '' ? \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::TAG_CLASS : \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::TAG_CLASS . str_replace('\\', '_', $className);
282 }
283
284 /**
285 * Instantiates all registered caches.
286 *
287 * @return void
288 */
289 protected function createAllCaches() {
290 foreach (array_keys($this->cacheConfigurations) as $identifier) {
291 if (!isset($this->caches[$identifier])) {
292 $this->createCache($identifier);
293 }
294 }
295 }
296
297 /**
298 * Instantiates the cache for $identifier.
299 *
300 * @param string $identifier
301 * @return void
302 */
303 protected function createCache($identifier) {
304 if (isset($this->cacheConfigurations[$identifier]['frontend'])) {
305 $frontend = $this->cacheConfigurations[$identifier]['frontend'];
306 } else {
307 $frontend = $this->defaultCacheConfiguration['frontend'];
308 }
309 if (isset($this->cacheConfigurations[$identifier]['backend'])) {
310 $backend = $this->cacheConfigurations[$identifier]['backend'];
311 } else {
312 $backend = $this->defaultCacheConfiguration['backend'];
313 }
314 if (isset($this->cacheConfigurations[$identifier]['options'])) {
315 $backendOptions = $this->cacheConfigurations[$identifier]['options'];
316 } else {
317 $backendOptions = $this->defaultCacheConfiguration['options'];
318 }
319 $this->cacheFactory->create($identifier, $frontend, $backend, $backendOptions);
320 }
321
322 }