[BUGFIX] ClassLoader not working with NullBackend for legacy classes
[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 text file 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 * @return ClassAliasMap
127 */
128 public function setPackages(array $packages) {
129 $this->packages = $packages;
130 return $this;
131 }
132
133 /**
134 * Load early instance mapping
135 *
136 * @return boolean
137 */
138 public function loadEarlyInstanceMappingFromCache() {
139 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
140 if (!$cacheEntryIdentifier !== NULL && $this->coreCache->has($cacheEntryIdentifier)) {
141 return (boolean)$this->coreCache->requireOnce($cacheEntryIdentifier);
142 }
143 return FALSE;
144 }
145
146 /**
147 * Build mapping for early instances
148 *
149 * @return array
150 */
151 public function buildMappingAndInitializeEarlyInstanceMapping() {
152 $aliasToClassNameMapping = array();
153 foreach ($this->packages as $package) {
154 if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
155 $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $package->getClassAliases());
156 }
157 }
158 $lowercasedAliasToClassNameMapping = array();
159 foreach ($aliasToClassNameMapping as $aliasClassName => $className) {
160 $lowercasedAliasToClassNameMapping[strtolower($aliasClassName)] = $className;
161 }
162 $aliasToClassNameMapping = $lowercasedAliasToClassNameMapping;
163 $classNameToAliasMapping = array();
164 foreach (array_flip($aliasToClassNameMapping) as $className => $aliasClassName) {
165 $lookUpClassName = strtolower($className);
166 if (!isset($classNameToAliasMapping[$lookUpClassName])) {
167 $classNameToAliasMapping[$lookUpClassName] = array();
168 }
169 $classNameToAliasMapping[$lookUpClassName][] = $aliasClassName;
170 }
171
172 $this->buildEarlyInstanceMappingAndSaveToCache($aliasToClassNameMapping);
173
174 $classNameToAliasMapping = array();
175 foreach ($aliasToClassNameMapping as $aliasClassName => $originalClassName) {
176 $classNameToAliasMapping[$originalClassName][$aliasClassName] = $aliasClassName;
177 }
178
179 return $classNameToAliasMapping;
180 }
181
182 /**
183 * Build mapping files
184 *
185 * @param array $classNameToAliasMapping
186 * @return void
187 */
188 public function buildMappingFiles(array $classNameToAliasMapping) {
189 foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
190 $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
191 // Trigger autoloading for all aliased class names, so a cache entry is created
192 $classLoadingInformation = $this->classLoader->buildClassLoadingInformation($originalClassName);
193 if (NULL !== $classLoadingInformation) {
194 $classLoadingInformation = implode("\xff", array_merge($classLoadingInformation, $aliasClassNames));
195 $this->classesCache->set($originalClassNameCacheEntryIdentifier, $classLoadingInformation);
196 foreach ($aliasClassNames as $aliasClassName) {
197 $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
198 $this->classesCache->set($aliasClassNameCacheEntryIdentifier, $classLoadingInformation);
199 }
200 }
201 }
202 }
203
204 /**
205 * Build and save mapping files to cache
206 *
207 * @param array $aliasToClassNameMapping
208 * @return void
209 */
210 protected function buildEarlyInstanceMappingAndSaveToCache(array $aliasToClassNameMapping) {
211 $classedLoadedPriorToClassLoader = array_intersect($aliasToClassNameMapping, array_merge(get_declared_classes(), get_declared_interfaces()));
212 if (!empty($classedLoadedPriorToClassLoader)) {
213 $proxyContent = array($this->buildClassLoaderCommand());
214 foreach ($classedLoadedPriorToClassLoader as $aliasClassName => $originalClassName) {
215 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
216 }
217 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
218 if ($cacheEntryIdentifier !== NULL) {
219 $this->coreCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
220 if ($this->coreCache->requireOnce($cacheEntryIdentifier) === NULL) {
221 // Just happens on TYPO3\CMS\Core\Cache\Backend\NullBackend, cause we don't get a return value.
222 eval(implode(PHP_EOL, $proxyContent));
223 }
224 } else {
225 eval(implode(PHP_EOL, $proxyContent));
226 }
227 }
228 }
229
230 /**
231 * String command to build class loader
232 *
233 * @return string
234 */
235 protected function buildClassLoaderCommand() {
236 return '$classLoader = \\TYPO3\\CMS\\Core\\Core\\Bootstrap::getInstance()->getEarlyInstance(\'TYPO3\\CMS\\Core\\Core\\ClassLoader\');';
237 }
238
239 /**
240 * String command to build class alias
241 *
242 * @param string $aliasClassName
243 * @param string $originalClassName
244 * @return string
245 */
246 protected function buildAliasCommand($aliasClassName, $originalClassName) {
247 return sprintf('%s->setAliasForClassName(\'%s\', \'%s\');', '$classLoader', $aliasClassName, $originalClassName);
248 }
249
250 /**
251 * Creates a require_once command for the given file.
252 *
253 * @param string $requiredFile
254 * @return string
255 */
256 protected function buildRequireOnceCommand($requiredFile) {
257 return sprintf('require_once \'%s\';', $requiredFile);
258 }
259
260 /**
261 * Set an alias for a class name
262 *
263 * @param string $aliasClassName
264 * @param string $originalClassName
265 * @return bool true on success or false on failure
266 */
267 public function setAliasForClassName($aliasClassName, $originalClassName) {
268 if (isset($this->aliasToClassNameMapping[$lowercasedAliasClassName = strtolower($aliasClassName)])) {
269 return TRUE;
270 }
271 $this->aliasToClassNameMapping[$lowercasedAliasClassName] = $originalClassName;
272 $this->classNameToAliasMapping[strtolower($originalClassName)][$lowercasedAliasClassName] = $aliasClassName;
273 return (\class_exists($aliasClassName, FALSE) || \interface_exists($aliasClassName, FALSE)) ? TRUE : class_alias($originalClassName, $aliasClassName);
274 }
275
276 /**
277 * Get final class name of alias
278 *
279 * @param string $alias
280 * @return mixed
281 */
282 public function getClassNameForAlias($alias) {
283 $lookUpClassName = strtolower($alias);
284 return isset($this->aliasToClassNameMapping[$lookUpClassName]) ? $this->aliasToClassNameMapping[$lookUpClassName] : $alias;
285 }
286
287
288 /**
289 * Get list of aliases for class name
290 *
291 * @param string $className
292 * @return mixed
293 */
294 public function getAliasesForClassName($className) {
295 $lookUpClassName = strtolower($className);
296 return isset($this->classNameToAliasMapping[$lookUpClassName]) ? $this->classNameToAliasMapping[$lookUpClassName] : array($className);
297 }
298 }