[TASK] Replace ClassLoader cache with standard caches
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Core / ClassAliasMap.php
1 <?php
2 namespace TYPO3\CMS\Core\Core;
3 /***************************************************************
4 * Copyright notice
5 *
6 * (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28
29 /**
30 * This class is responsible for setting and containing class aliases
31 */
32 class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
33
34 /**
35 * Old class name to new class name mapping
36 *
37 * @var array
38 */
39 protected $aliasToClassNameMapping = array();
40
41 /**
42 * New class name to old class name mapping
43 *
44 * @var array
45 */
46 protected $classNameToAliasMapping = array();
47
48 /**
49 * @var \TYPO3\CMS\Core\Cache\Frontend\StringFrontend
50 */
51 protected $classesCache;
52
53 /**
54 * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
55 */
56 protected $coreCache;
57
58 /**
59 * @var ClassLoader
60 */
61 protected $classLoader;
62
63 /**
64 * @var string Cache identifier
65 */
66 protected $cacheIdentifier;
67
68 /**
69 * @var \TYPO3\Flow\Package\Package[]
70 */
71 protected $packages = array();
72
73 /**
74 * @param \TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache
75 */
76 public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache) {
77 $this->classesCache = $classesCache;
78 }
79
80 /**
81 * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
82 */
83 public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache) {
84 $this->coreCache = $coreCache;
85 }
86
87 /**
88 * @param ClassLoader
89 */
90 public function injectClassLoader(ClassLoader $classLoader) {
91 $this->classLoader = $classLoader;
92 }
93
94 /**
95 * @return string
96 */
97 protected function getCacheIdentifier() {
98 return $this->cacheIdentifier;
99 }
100
101 /**
102 * Set cache identifier
103 *
104 * @param string $cacheIdentifier
105 * @return ClassAliasMap
106 */
107 public function setCacheIdentifier($cacheIdentifier) {
108 $this->cacheIdentifier = $cacheIdentifier;
109 return $this;
110 }
111
112 /**
113 * Get cache identifier
114 *
115 * @return string
116 */
117 protected function getCacheEntryIdentifier() {
118 $cacheIdentifier = $this->getCacheIdentifier();
119 return $cacheIdentifier !== NULL ? 'ClassAliasMap_' . TYPO3_MODE . '_' . $cacheIdentifier : NULL;
120 }
121
122 /**
123 * Set packages
124 *
125 * @param array $packages
126 */
127 public function setPackages(array $packages) {
128 $this->packages = $packages;
129 if (!$this->loadEarlyInstanceMappingFromCache()) {
130 $classNameToAliasMapping = $this->buildMappingAndInitializeEarlyInstanceMapping();
131 $this->buildMappingFiles($classNameToAliasMapping);
132 }
133 }
134
135 /**
136 * Return class name to alias mapping
137 *
138 * @param array $packages
139 * @return array
140 */
141 public function setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead(array $packages) {
142 $this->packages = $packages;
143 return $this->buildMappingAndInitializeEarlyInstanceMapping();
144 }
145
146 /**
147 * Load early instance mapping
148 *
149 * @return boolean
150 */
151 protected function loadEarlyInstanceMappingFromCache() {
152 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
153 if (!$cacheEntryIdentifier !== NULL && $this->coreCache->has($cacheEntryIdentifier)) {
154 return (bool) $this->coreCache->requireOnce($cacheEntryIdentifier);
155 }
156 return FALSE;
157 }
158
159 /**
160 * Build mapping for early instances
161 *
162 * @return array
163 */
164 protected function buildMappingAndInitializeEarlyInstanceMapping() {
165 $aliasToClassNameMapping = array();
166 foreach ($this->packages as $package) {
167 if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
168 $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $package->getClassAliases());
169 }
170 }
171 $lowercasedAliasToClassNameMapping = array();
172 foreach ($aliasToClassNameMapping as $aliasClassName => $className) {
173 $lowercasedAliasToClassNameMapping[strtolower($aliasClassName)] = $className;
174 }
175 $aliasToClassNameMapping = $lowercasedAliasToClassNameMapping;
176 $classNameToAliasMapping = array();
177 foreach (array_flip($aliasToClassNameMapping) as $className => $aliasClassName) {
178 $lookUpClassName = strtolower($className);
179 if (!isset($classNameToAliasMapping[$lookUpClassName])) {
180 $classNameToAliasMapping[$lookUpClassName] = array();
181 }
182 $classNameToAliasMapping[$lookUpClassName][] = $aliasClassName;
183 }
184
185 $this->buildEarlyInstanceMappingAndSaveToCache($aliasToClassNameMapping);
186
187 $classNameToAliasMapping = array();
188 foreach ($aliasToClassNameMapping as $aliasClassName => $originalClassName) {
189 $classNameToAliasMapping[$originalClassName][$aliasClassName] = $aliasClassName;
190 }
191
192 return $classNameToAliasMapping;
193 }
194
195 /**
196 * Build mapping files
197 *
198 * @param array $classNameToAliasMapping
199 * @return void
200 */
201 public function buildMappingFiles(array $classNameToAliasMapping) {
202 foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
203 $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
204 // Trigger autoloading for all aliased class names, so a cache entry is created
205 $classLoadingInformation = $this->classLoader->buildClassLoadingInformation($originalClassName);
206 if (NULL !== $classLoadingInformation) {
207 $classLoadingInformation = implode("\xff", array_merge($classLoadingInformation, $aliasClassNames));
208 $this->classesCache->set($originalClassNameCacheEntryIdentifier, $classLoadingInformation);
209 foreach ($aliasClassNames as $aliasClassName) {
210 $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
211 $this->classesCache->set($aliasClassNameCacheEntryIdentifier, $classLoadingInformation);
212 }
213 }
214 }
215 }
216
217 /**
218 * Build and save mapping files to cache
219 *
220 * @param array $aliasToClassNameMapping
221 * @return void
222 */
223 protected function buildEarlyInstanceMappingAndSaveToCache(array $aliasToClassNameMapping) {
224 $classedLoadedPriorToClassLoader = array_intersect($aliasToClassNameMapping, array_merge(get_declared_classes(), get_declared_interfaces()));
225 if (!empty($classedLoadedPriorToClassLoader)) {
226 $proxyContent = array($this->buildClassLoaderCommand());
227 foreach ($classedLoadedPriorToClassLoader as $aliasClassName => $originalClassName) {
228 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
229 }
230 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
231 if ($cacheEntryIdentifier !== NULL) {
232 $this->coreCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
233 $this->coreCache->requireOnce($cacheEntryIdentifier);
234 } else {
235 eval(implode(PHP_EOL, $proxyContent));
236 }
237 }
238 }
239
240 /**
241 * String command to build class loader
242 *
243 * @return string
244 */
245 protected function buildClassLoaderCommand() {
246 return '$classLoader = \\TYPO3\\CMS\\Core\\Core\\Bootstrap::getInstance()->getEarlyInstance(\'TYPO3\\CMS\\Core\\Core\\ClassLoader\');';
247 }
248
249 /**
250 * String command to build class alias
251 *
252 * @param string $aliasClassName
253 * @param string $originalClassName
254 * @return string
255 */
256 protected function buildAliasCommand($aliasClassName, $originalClassName) {
257 return sprintf('%s->setAliasForClassName(\'%s\', \'%s\');', '$classLoader', $aliasClassName, $originalClassName);
258 }
259
260 /**
261 * Creates a require_once command for the given file.
262 *
263 * @param string $requiredFile
264 * @return string
265 */
266 protected function buildRequireOnceCommand($requiredFile) {
267 return sprintf('require_once \'%s\';', $requiredFile);
268 }
269
270 /**
271 * Set an alias for a class name
272 *
273 * @param string $aliasClassName
274 * @param string $originalClassName
275 * @return bool true on success or false on failure
276 */
277 public function setAliasForClassName($aliasClassName, $originalClassName) {
278 if (isset($this->aliasToClassNameMapping[$lowercasedAliasClassName = strtolower($aliasClassName)])) {
279 return TRUE;
280 }
281 $this->aliasToClassNameMapping[$lowercasedAliasClassName] = $originalClassName;
282 $this->classNameToAliasMapping[strtolower($originalClassName)][$lowercasedAliasClassName] = $aliasClassName;
283 return (\class_exists($aliasClassName, FALSE) || \interface_exists($aliasClassName, FALSE)) ? TRUE : class_alias($originalClassName, $aliasClassName);
284 }
285
286 /**
287 * Get final class name of alias
288 *
289 * @param string $alias
290 * @return mixed
291 */
292 public function getClassNameForAlias($alias) {
293 $lookUpClassName = strtolower($alias);
294 return isset($this->aliasToClassNameMapping[$lookUpClassName]) ? $this->aliasToClassNameMapping[$lookUpClassName] : $alias;
295 }
296
297
298 /**
299 * Get list of aliases for class name
300 *
301 * @param string $className
302 * @return mixed
303 */
304 public function getAliasesForClassName($className) {
305 $lookUpClassName = strtolower($className);
306 return isset($this->classNameToAliasMapping[$lookUpClassName]) ? $this->classNameToAliasMapping[$lookUpClassName] : array($className);
307 }
308 }