[FEATURE] Integrate preliminary PackageManager API
[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 ($this->classLoader->loadClass($originalClassName, FALSE) && $originalClassTarget = $cacheBackend->getTargetOfLinkedCacheEntry($originalClassNameCacheEntryIdentifier)) {
196 $proxyContent = array(
197 $this->buildRequireOnceCommand($originalClassTarget),
198 $this->buildClassLoaderCommand(),
199 );
200 foreach ($aliasClassNames as $aliasClassName) {
201 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
202 }
203 $this->classesCache->set($originalClassNameCacheEntryIdentifier, implode(LF, $proxyContent));
204 }
205 }
206 foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
207 foreach ($aliasClassNames as $aliasClassName) {
208 $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
209 $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
210 if ($this->classesCache->has($aliasClassNameCacheEntryIdentifier)) {
211 $this->classesCache->remove($aliasClassNameCacheEntryIdentifier);
212 }
213 // Link all aliases to original cache entry
214 if ($this->classesCache->has($originalClassNameCacheEntryIdentifier)) {
215 $cacheBackend->setLinkToOtherCacheEntry($aliasClassNameCacheEntryIdentifier, $originalClassNameCacheEntryIdentifier);
216 }
217 }
218 }
219 }
220
221 /**
222 * Build and save mapping files to cache
223 *
224 * @param array $aliasToClassNameMapping
225 * @return void
226 */
227 protected function buildEarlyInstanceMappingAndSaveToCache(array $aliasToClassNameMapping) {
228 $classedLoadedPriorToClassLoader = array_intersect($aliasToClassNameMapping, array_merge(get_declared_classes(), get_declared_interfaces()));
229 if (!empty($classedLoadedPriorToClassLoader)) {
230 $proxyContent = array($this->buildClassLoaderCommand());
231 foreach ($classedLoadedPriorToClassLoader as $aliasClassName => $originalClassName) {
232 $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
233 }
234 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
235 if ($cacheEntryIdentifier !== NULL) {
236 $this->classesCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
237 $this->classesCache->requireOnce($cacheEntryIdentifier);
238 } else {
239 eval(implode(PHP_EOL, $proxyContent));
240 }
241 }
242 }
243
244 /**
245 * String command to build class loader
246 *
247 * @return string
248 */
249 protected function buildClassLoaderCommand() {
250 return '$classLoader = \\TYPO3\\CMS\\Core\\Core\\Bootstrap::getInstance()->getEarlyInstance(\'TYPO3\\CMS\\Core\\Core\\ClassLoader\');';
251 }
252
253 /**
254 * String command to build class alias
255 *
256 * @param string $aliasClassName
257 * @param string $originalClassName
258 * @return string
259 */
260 protected function buildAliasCommand($aliasClassName, $originalClassName) {
261 return sprintf('%s->setAliasForClassName(\'%s\', \'%s\');', '$classLoader', $aliasClassName, $originalClassName);
262 }
263
264 /**
265 * String command to build class alias
266 *
267 * @param string $classFilePath
268 * @return string
269 */
270 protected function buildRequireOnceCommand($classFilePath) {
271 return sprintf('require_once __DIR__ . \'/%s\';', $classFilePath);
272 }
273
274 /**
275 * Set an alias for a class name
276 *
277 * @param string $aliasClassName
278 * @param string $originalClassName
279 * @return bool true on success or false on failure
280 */
281 public function setAliasForClassName($aliasClassName, $originalClassName) {
282 if (isset($this->aliasToClassNameMapping[$lowercasedAliasClassName = strtolower($aliasClassName)])) {
283 return TRUE;
284 }
285 $this->aliasToClassNameMapping[$lowercasedAliasClassName] = $originalClassName;
286 $this->classNameToAliasMapping[strtolower($originalClassName)][$lowercasedAliasClassName] = $aliasClassName;
287 return (\class_exists($aliasClassName, FALSE) || \interface_exists($aliasClassName, FALSE)) ? TRUE : class_alias($originalClassName, $aliasClassName);
288 }
289
290 /**
291 * Get final class name of alias
292 *
293 * @param string $alias
294 * @return mixed
295 */
296 public function getClassNameForAlias($alias) {
297 $lookUpClassName = strtolower($alias);
298 return isset($this->aliasToClassNameMapping[$lookUpClassName]) ? $this->aliasToClassNameMapping[$lookUpClassName] : $alias;
299 }
300
301
302 /**
303 * Get list of aliases for class name
304 *
305 * @param string $className
306 * @return mixed
307 */
308 public function getAliasesForClassName($className) {
309 $lookUpClassName = strtolower($className);
310 return isset($this->classNameToAliasMapping[$lookUpClassName]) ? $this->classNameToAliasMapping[$lookUpClassName] : array($className);
311 }
312 }