450ff7fb144a7f0a28098215f4c507955b064201
[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\PhpFrontend
50 */
51 protected $classesCache;
52
53 /**
54 * @var \TYPO3\CMS\Core\Core\ClassLoader
55 */
56 protected $classLoader;
57
58 /**
59 * @var string Cache identifier
60 */
61 protected $cacheIdentifier;
62
63 /**
64 * @var array<\TYPO3\Flow\Package\Package>
65 */
66 protected $packages = array();
67
68 /**
69 * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache
70 */
71 public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache) {
72 $this->classesCache = $classesCache;
73 }
74
75 /**
76 * @param \TYPO3\CMS\Core\Core\ClassLoader
77 */
78 public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader $classLoader) {
79 $this->classLoader = $classLoader;
80 }
81
82 /**
83 * @return string
84 */
85 protected function getCacheIdentifier() {
86 return $this->cacheIdentifier;
87 }
88
89 /**
90 * Set cache identifier
91 *
92 * @param string $cacheIdentifier
93 * @return ClassAliasMap
94 */
95 public function setCacheIdentifier($cacheIdentifier) {
96 $this->cacheIdentifier = $cacheIdentifier;
97 return $this;
98 }
99
100 /**
101 * Get cache identifier
102 *
103 * @return string
104 */
105 protected function getCacheEntryIdentifier() {
106 $cacheIdentifier = $this->getCacheIdentifier();
107 return $cacheIdentifier !== NULL ? 'ClassAliasMap_' . TYPO3_MODE . '_' . $cacheIdentifier : NULL;
108 }
109
110 /**
111 * Set packages
112 *
113 * @param array $packages
114 */
115 public function setPackages(array $packages) {
116 $this->packages = $packages;
117 if (!$this->loadEarlyInstanceMappingFromCache()) {
118 $classNameToAliasMapping = $this->buildMappingAndInitializeEarlyInstanceMapping();
119 $this->buildMappingFiles($classNameToAliasMapping);
120 }
121 }
122
123 /**
124 * Return class name to alias mapping
125 *
126 * @param array $packages
127 * @return array
128 */
129 public function setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead(array $packages) {
130 $this->packages = $packages;
131 return $this->buildMappingAndInitializeEarlyInstanceMapping();
132 }
133
134 /**
135 * Load early instance mapping
136 *
137 * @return boolean
138 */
139 protected function loadEarlyInstanceMappingFromCache() {
140 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
141 if (!$cacheEntryIdentifier !== NULL && $this->classesCache->has($cacheEntryIdentifier)) {
142 return (bool) $this->classesCache->requireOnce($cacheEntryIdentifier);
143 }
144 return FALSE;
145 }
146
147 /**
148 * Build mapping for early instances
149 *
150 * @return array
151 */
152 protected function buildMappingAndInitializeEarlyInstanceMapping() {
153 $aliasToClassNameMapping = array();
154 foreach ($this->packages as $package) {
155 if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
156 $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $package->getClassAliases());
157 }
158 }
159 $lowercasedAliasToClassNameMapping = array();
160 foreach ($aliasToClassNameMapping as $aliasClassName => $className) {
161 $lowercasedAliasToClassNameMapping[strtolower($aliasClassName)] = $className;
162 }
163 $aliasToClassNameMapping = $lowercasedAliasToClassNameMapping;
164 $classNameToAliasMapping = array();
165 foreach (array_flip($aliasToClassNameMapping) as $className => $aliasClassName) {
166 $lookUpClassName = strtolower($className);
167 if (!isset($classNameToAliasMapping[$lookUpClassName])) {
168 $classNameToAliasMapping[$lookUpClassName] = array();
169 }
170 $classNameToAliasMapping[$lookUpClassName][] = $aliasClassName;
171 }
172
173 $this->buildEarlyInstanceMappingAndSaveToCache($aliasToClassNameMapping);
174
175 $classNameToAliasMapping = array();
176 foreach ($aliasToClassNameMapping as $aliasClassName => $originalClassName) {
177 $classNameToAliasMapping[$originalClassName][$aliasClassName] = $aliasClassName;
178 }
179
180 return $classNameToAliasMapping;
181 }
182
183 /**
184 * Build mapping files
185 *
186 * @param array $classNameToAliasMapping
187 * @return void
188 */
189 public function buildMappingFiles(array $classNameToAliasMapping) {
190 /** @var $cacheBackend \TYPO3\CMS\Core\Cache\Backend\ClassLoaderBackend */
191 $cacheBackend = $this->classesCache->getBackend();
192 foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
193 $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
194 // Trigger autoloading for all aliased class names, so a cache entry is created
195 if (
196 $this->classLoader->loadClass($originalClassName, FALSE)
197 && ($requiredFile = $cacheBackend->getPathOfRequiredFileInCacheEntry($originalClassNameCacheEntryIdentifier))
198 ) {
199 $proxyContent = array(
200 $this->buildRequireOnceCommand($requiredFile),
201 $this->buildClassLoaderCommand(),
202 );
203 foreach ($aliasClassNames as $aliasClassName) {
204 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
205 }
206 $this->classesCache->set($originalClassNameCacheEntryIdentifier, implode(LF, $proxyContent));
207 }
208 }
209 foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
210 foreach ($aliasClassNames as $aliasClassName) {
211 $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
212 $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
213 if ($this->classesCache->has($aliasClassNameCacheEntryIdentifier)) {
214 $this->classesCache->remove($aliasClassNameCacheEntryIdentifier);
215 }
216 // Link all aliases to original cache entry
217 if ($this->classesCache->has($originalClassNameCacheEntryIdentifier)) {
218 $cacheBackend->setLinkToOtherCacheEntry($aliasClassNameCacheEntryIdentifier, $originalClassNameCacheEntryIdentifier);
219 }
220 }
221 }
222 }
223
224 /**
225 * Build and save mapping files to cache
226 *
227 * @param array $aliasToClassNameMapping
228 * @return void
229 */
230 protected function buildEarlyInstanceMappingAndSaveToCache(array $aliasToClassNameMapping) {
231 $classedLoadedPriorToClassLoader = array_intersect($aliasToClassNameMapping, array_merge(get_declared_classes(), get_declared_interfaces()));
232 if (!empty($classedLoadedPriorToClassLoader)) {
233 $proxyContent = array($this->buildClassLoaderCommand());
234 foreach ($classedLoadedPriorToClassLoader as $aliasClassName => $originalClassName) {
235 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
236 }
237 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
238 if ($cacheEntryIdentifier !== NULL) {
239 $this->classesCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
240 $this->classesCache->requireOnce($cacheEntryIdentifier);
241 } else {
242 eval(implode(PHP_EOL, $proxyContent));
243 }
244 }
245 }
246
247 /**
248 * String command to build class loader
249 *
250 * @return string
251 */
252 protected function buildClassLoaderCommand() {
253 return '$classLoader = \\TYPO3\\CMS\\Core\\Core\\Bootstrap::getInstance()->getEarlyInstance(\'TYPO3\\CMS\\Core\\Core\\ClassLoader\');';
254 }
255
256 /**
257 * String command to build class alias
258 *
259 * @param string $aliasClassName
260 * @param string $originalClassName
261 * @return string
262 */
263 protected function buildAliasCommand($aliasClassName, $originalClassName) {
264 return sprintf('%s->setAliasForClassName(\'%s\', \'%s\');', '$classLoader', $aliasClassName, $originalClassName);
265 }
266
267 /**
268 * Creates a require_once command for the given file.
269 *
270 * @param string $requiredFile
271 * @return string
272 */
273 protected function buildRequireOnceCommand($requiredFile) {
274 return sprintf('require_once \'%s\';', $requiredFile);
275 }
276
277 /**
278 * Set an alias for a class name
279 *
280 * @param string $aliasClassName
281 * @param string $originalClassName
282 * @return bool true on success or false on failure
283 */
284 public function setAliasForClassName($aliasClassName, $originalClassName) {
285 if (isset($this->aliasToClassNameMapping[$lowercasedAliasClassName = strtolower($aliasClassName)])) {
286 return TRUE;
287 }
288 $this->aliasToClassNameMapping[$lowercasedAliasClassName] = $originalClassName;
289 $this->classNameToAliasMapping[strtolower($originalClassName)][$lowercasedAliasClassName] = $aliasClassName;
290 return (\class_exists($aliasClassName, FALSE) || \interface_exists($aliasClassName, FALSE)) ? TRUE : class_alias($originalClassName, $aliasClassName);
291 }
292
293 /**
294 * Get final class name of alias
295 *
296 * @param string $alias
297 * @return mixed
298 */
299 public function getClassNameForAlias($alias) {
300 $lookUpClassName = strtolower($alias);
301 return isset($this->aliasToClassNameMapping[$lookUpClassName]) ? $this->aliasToClassNameMapping[$lookUpClassName] : $alias;
302 }
303
304
305 /**
306 * Get list of aliases for class name
307 *
308 * @param string $className
309 * @return mixed
310 */
311 public function getAliasesForClassName($className) {
312 $lookUpClassName = strtolower($className);
313 return isset($this->classNameToAliasMapping[$lookUpClassName]) ? $this->classNameToAliasMapping[$lookUpClassName] : array($className);
314 }
315 }