[BUGFIX] Class loader handles information about invalid classes
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Core / ClassLoader.php
1 <?php
2 namespace TYPO3\CMS\Core\Core;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
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 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Locking\Locker;
31 use TYPO3\CMS\Core\Package\PackageInterface;
32 use TYPO3\CMS\Core\Utility\GeneralUtility;
33 use TYPO3\CMS\Core\Cache;
34
35 /**
36 * Class Loader implementation which loads .php files found in the classes
37 * directory of an object.
38 */
39 class ClassLoader {
40
41 /**
42 * @var ClassAliasMap
43 */
44 protected $classAliasMap;
45
46 /**
47 * @var ClassAliasMap
48 */
49 static protected $staticAliasMap;
50
51 /**
52 * @var \TYPO3\CMS\Core\Cache\Frontend\StringFrontend
53 */
54 protected $classesCache;
55
56 /**
57 * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
58 */
59 protected $coreCache;
60
61 /**
62 * @var string
63 */
64 protected $cacheIdentifier;
65
66 /**
67 * @var \TYPO3\Flow\Package\Package[]
68 */
69 protected $packages = array();
70
71 /**
72 * @var bool
73 */
74 protected $isEarlyCache = TRUE;
75
76 /**
77 * @var array
78 */
79 protected $runtimeClassLoadingInformationCache = array();
80
81 /**
82 * @var array A list of namespaces this class loader is definitely responsible for
83 */
84 protected $packageNamespaces = array();
85
86 /**
87 * @var array A list of packages and their replaces pointing to class paths
88 */
89 protected $packageClassesPaths = array();
90
91 /**
92 * @var bool Is TRUE while loading the Locker class to prevent a deadlock in the implicit call to loadClass
93 */
94 protected $isLoadingLocker = FALSE;
95
96 /**
97 * @var \TYPO3\CMS\Core\Locking\Locker
98 */
99 protected $lockObject = NULL;
100
101 /**
102 * Constructor
103 *
104 * @param ApplicationContext $context
105 */
106 public function __construct(ApplicationContext $context) {
107 $this->classesCache = new Cache\Frontend\StringFrontend('cache_classes', new Cache\Backend\TransientMemoryBackend($context));
108 }
109
110 /**
111 * Get class alias map list injected
112 *
113 * @param ClassAliasMap
114 * @return void
115 */
116 public function injectClassAliasMap(ClassAliasMap $classAliasMap) {
117 $this->classAliasMap = $classAliasMap;
118 static::$staticAliasMap = $classAliasMap;
119 }
120
121 /**
122 * Get core cache injected
123 *
124 * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
125 * @return void
126 */
127 public function injectCoreCache(Cache\Frontend\PhpFrontend $coreCache) {
128 $this->coreCache = $coreCache;
129 $this->classAliasMap->injectCoreCache($coreCache);
130 }
131
132 /**
133 * Get classes cache injected
134 *
135 * @param \TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache
136 * @return void
137 */
138 public function injectClassesCache(Cache\Frontend\StringFrontend $classesCache) {
139 $earlyClassesCache = $this->classesCache;
140 $this->classesCache = $classesCache;
141 $this->isEarlyCache = FALSE;
142 $this->classAliasMap->injectClassesCache($classesCache);
143 foreach ($earlyClassesCache->getByTag('early') as $originalClassLoadingInformation) {
144 $classLoadingInformation = explode("\xff", $originalClassLoadingInformation);
145 $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $classLoadingInformation[1]));
146 if (!$this->classesCache->has($cacheEntryIdentifier)) {
147 $this->classesCache->set($cacheEntryIdentifier, $originalClassLoadingInformation);
148 }
149 }
150 }
151
152 /**
153 * Loads php files containing classes or interfaces found in the classes directory of
154 * a package and specifically registered classes.
155 *
156 * Caution: This function may be called "recursively" by the spl_autoloader if a class depends on another classes.
157 *
158 * @param string $className Name of the class/interface to load
159 * @return bool
160 */
161 public function loadClass($className) {
162 if ($className[0] === '\\') {
163 $className = substr($className, 1);
164 }
165
166 if (!$this->isValidClassName($className)) {
167 return FALSE;
168 }
169
170 $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $className));
171 $classLoadingInformation = $this->getClassLoadingInformationFromCache($cacheEntryIdentifier);
172 // Handle a cache miss
173 if ($classLoadingInformation === FALSE) {
174 $classLoadingInformation = $this->buildCachedClassLoadingInformation($cacheEntryIdentifier, $className);
175 }
176
177 // Class loading information structure
178 // array(
179 // 0 => class file path
180 // 1 => original class name
181 // 2 and following => alias class names
182 // )
183 $loadingSuccessful = FALSE;
184 if (is_array($classLoadingInformation) && count($classLoadingInformation) > 0) {
185 // The call to class_exists fixes a rare case when early instances need to be aliased
186 // but PHP fails to recognize the real path of the class. See #55904
187 $loadingSuccessful = class_exists($classLoadingInformation[1], FALSE) || (bool)require_once $classLoadingInformation[0];
188 }
189 if ($loadingSuccessful && count($classLoadingInformation) > 2) {
190 $originalClassName = $classLoadingInformation[1];
191 foreach (array_slice($classLoadingInformation, 2) as $aliasClassName) {
192 $this->setAliasForClassName($aliasClassName, $originalClassName);
193 }
194 }
195
196 return $loadingSuccessful;
197 }
198
199 /**
200 * Get class loading information for the given identifier for cache
201 * Return values:
202 * - array with class information (empty if the class is invalid)
203 * - FALSE if no class information is found in cache (cache miss)
204 * - NULL if the cache identifier is invalid (cache failure)
205 *
206 * @param string $cacheEntryIdentifier The identifier to fetch entry from cache
207 * @return array|FALSE|NULL The class information, empty array if class is unkown or FALSE if class information was not found in cache. NULL if a cache identifier is invalid.
208 */
209 public function getClassLoadingInformationFromCache($cacheEntryIdentifier) {
210 try {
211 $rawClassLoadingInformation = $this->classesCache->get($cacheEntryIdentifier);
212 } catch (\InvalidArgumentException $exception) {
213 return NULL;
214 }
215
216 if ($rawClassLoadingInformation === '') {
217 return array();
218 }
219
220 if ($rawClassLoadingInformation) {
221 return explode("\xff", $rawClassLoadingInformation);
222 }
223 return FALSE;
224 }
225
226 /**
227 * Builds the class loading information and writes it to the cache. It handles Locking for this cache.
228 *
229 * Caution: The function loadClass can be called "recursively" by spl_autoloader. This needs to be observed when
230 * locking for cache access. Only the first call to loadClass may acquire and release the lock!
231 *
232 * @param string $cacheEntryIdentifier Cache identifier for this class
233 * @param string $className Name of class this information is for
234 *
235 * @return array|FALSE|NULL The class information, empty array if class is unkown or FALSE if class information was not found in cache. NULL if a cache identifier is invalid.
236 */
237 protected function buildCachedClassLoadingInformation($cacheEntryIdentifier, $className) {
238 // We do not need locking if we are in earlyCache mode
239 $didLock = FALSE;
240 if (!$this->isEarlyCache) {
241 $didLock = $this->acquireLock();
242 }
243
244 // Look again into the cache after we got the lock, data might have been generated meanwhile
245 $classLoadingInformation = $this->getClassLoadingInformationFromCache($cacheEntryIdentifier);
246 // Handle repeated cache miss
247 if ($classLoadingInformation === FALSE) {
248 // Generate class information
249 $classLoadingInformation = $this->buildClassLoadingInformation($className);
250
251 if ($classLoadingInformation !== FALSE) {
252 // If we found class information, cache it
253 $this->classesCache->set(
254 $cacheEntryIdentifier,
255 implode("\xff", $classLoadingInformation),
256 $this->isEarlyCache ? array('early') : array()
257 );
258 } elseif (!$this->isEarlyCache) {
259 // Cache that the class is unknown
260 $this->classesCache->set($cacheEntryIdentifier, '');
261 }
262 }
263
264 $this->releaseLock($didLock);
265
266 return $classLoadingInformation;
267 }
268
269 /**
270 * Builds the class loading information
271 *
272 * @param string $className Name of class this information is for
273 *
274 * @return array|FALSE The class information or FALSE if class was not found
275 */
276 public function buildClassLoadingInformation($className) {
277 $classLoadingInformation = $this->buildClassLoadingInformationForClassFromCorePackage($className);
278
279 if ($classLoadingInformation === FALSE) {
280 $classLoadingInformation = $this->fetchClassLoadingInformationFromRuntimeCache($className);
281 }
282
283 if ($classLoadingInformation === FALSE) {
284 $classLoadingInformation = $this->buildClassLoadingInformationForClassFromRegisteredPackages($className);
285 }
286
287 if ($classLoadingInformation === FALSE) {
288 $classLoadingInformation = $this->buildClassLoadingInformationForClassByNamingConvention($className);
289 }
290
291 return $classLoadingInformation;
292 }
293
294 /**
295 * Find out if a class name is valid
296 *
297 * @param string $className
298 * @return bool
299 */
300 protected function isValidClassName($className) {
301 return strpos($className, ' ') === FALSE;
302 }
303
304 /**
305 * Retrieve class loading information for class from core package
306 *
307 * @param string $className
308 * @return array|FALSE
309 */
310 protected function buildClassLoadingInformationForClassFromCorePackage($className) {
311 if (substr($className, 0, 14) === 'TYPO3\\CMS\\Core') {
312 $classesFolder = substr($className, 15, 5) === 'Tests' ? '' : 'Classes/';
313 $classFilePath = PATH_typo3 . 'sysext/core/' . $classesFolder . str_replace('\\', '/', substr($className, 15)) . '.php';
314 if (@file_exists($classFilePath)) {
315 return array($classFilePath, $className);
316 }
317 }
318 return FALSE;
319 }
320
321 /**
322 * Retrieve class loading information from early class name autoload registry cache
323 *
324 * @param string $className
325 * @return array|FALSE
326 */
327 protected function fetchClassLoadingInformationFromRuntimeCache($className) {
328 $lowercasedClassName = strtolower($className);
329 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName])) {
330 return FALSE;
331 }
332 $classInformation = $this->runtimeClassLoadingInformationCache[$lowercasedClassName];
333 return @file_exists($classInformation[0]) ? $classInformation : FALSE;
334 }
335
336 /**
337 * Retrieve class loading information from registered packages
338 *
339 * @param string $className
340 * @return array|FALSE
341 */
342 protected function buildClassLoadingInformationForClassFromRegisteredPackages($className) {;
343 foreach ($this->packageNamespaces as $packageNamespace => $packageData) {
344 if (substr(str_replace('_', '\\', $className), 0, $packageData['namespaceLength']) === $packageNamespace) {
345 if ($packageData['substituteNamespaceInPath']) {
346 // If it's a TYPO3 package, classes don't comply to PSR-0.
347 // The namespace part is substituted.
348 $classPathAndFilename = '/' . str_replace('\\', '/', ltrim(substr($className, $packageData['namespaceLength']), '\\')) . '.php';
349 } else {
350 // Make the classname PSR-0 compliant by replacing underscores only in the classname not in the namespace
351 $classPathAndFilename = '';
352 $lastNamespacePosition = strrpos($className, '\\');
353 if ($lastNamespacePosition !== FALSE) {
354 $namespace = substr($className, 0, $lastNamespacePosition);
355 $className = substr($className, $lastNamespacePosition + 1);
356 $classPathAndFilename = str_replace('\\', '/', $namespace) . '/';
357 }
358 $classPathAndFilename .= str_replace('_', '/', $className) . '.php';
359 }
360 if (strtolower(substr($className, $packageData['namespaceLength'], 5)) === 'tests') {
361 $classPathAndFilename = $packageData['packagePath'] . $classPathAndFilename;
362 } else {
363 $classPathAndFilename = $packageData['classesPath'] . $classPathAndFilename;
364 }
365 if (@file_exists($classPathAndFilename)) {
366 return array($classPathAndFilename, $className);
367 }
368 }
369 }
370 return FALSE;
371 }
372
373 /**
374 * Retrieve class loading information based on 'extbase' naming convention into the registry.
375 *
376 * @param string $className Class name to find source file of
377 * @return array|FALSE
378 */
379 protected function buildClassLoadingInformationForClassByNamingConvention($className) {
380 $delimiter = '_';
381 // To handle namespaced class names, split the class name at the
382 // namespace delimiters.
383 if (strpos($className, '\\') !== FALSE) {
384 $delimiter = '\\';
385 }
386
387 $classNameParts = explode($delimiter, $className, 4);
388
389 // We only handle classes that follow the convention Vendor\Product\Classname or is longer
390 // so we won't deal with class names that only have one or two parts
391 if (count($classNameParts) <= 2) {
392 return FALSE;
393 }
394
395 if (
396 isset($classNameParts[0])
397 && isset($classNameParts[1])
398 && $classNameParts[0] === 'TYPO3'
399 && $classNameParts[1] === 'CMS'
400 ) {
401 $extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($classNameParts[2]);
402 $classNameWithoutVendorAndProduct = $classNameParts[3];
403 } else {
404 $extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($classNameParts[1]);
405 $classNameWithoutVendorAndProduct = $classNameParts[2];
406
407 if (isset($classNameParts[3])) {
408 $classNameWithoutVendorAndProduct .= $delimiter . $classNameParts[3];
409 }
410 }
411
412 if ($extensionKey && isset($this->packageClassesPaths[$extensionKey])) {
413 if (substr(strtolower($classNameWithoutVendorAndProduct), 0, 5) === 'tests') {
414 $classesPath = $this->packages[$extensionKey]->getPackagePath();
415 } else {
416 $classesPath = $this->packageClassesPaths[$extensionKey];
417 }
418 $classFilePath = $classesPath . strtr($classNameWithoutVendorAndProduct, $delimiter, '/') . '.php';
419 if (@file_exists($classFilePath)) {
420 return array($classFilePath, $className);
421 }
422 }
423
424 return FALSE;
425 }
426
427 /**
428 * Get cache entry identifier for the package namespaces cache
429 *
430 * @return string|NULL identifier
431 */
432 protected function getCacheEntryIdentifier() {
433 return $this->cacheIdentifier !== NULL
434 ? 'ClassLoader_' . $this->cacheIdentifier
435 : NULL;
436 }
437
438 /**
439 * Set cache identifier
440 *
441 * @param string $cacheIdentifier Cache identifier for package namespaces cache
442 * @return ClassLoader
443 */
444 public function setCacheIdentifier($cacheIdentifier) {
445 $this->cacheIdentifier = $cacheIdentifier;
446 return $this;
447 }
448
449 /**
450 * Sets the available packages
451 *
452 * @param array $packages An array of \TYPO3\Flow\Package\Package objects
453 * @return ClassLoader
454 */
455 public function setPackages(array $packages) {
456 $this->packages = $packages;
457
458 if (!$this->loadPackageNamespacesFromCache()) {
459 $this->buildPackageNamespacesAndClassesPaths();
460 } else {
461 $this->classAliasMap->setPackages($packages);
462 }
463 // Clear the runtime cache for runtime activated packages
464 $this->runtimeClassLoadingInformationCache = array();
465 return $this;
466 }
467
468 /**
469 * Add a package to class loader just during runtime, so classes can be loaded without the need for a new request
470 *
471 * @param \TYPO3\Flow\Package\PackageInterface $package
472 * @return ClassLoader
473 */
474 public function addActivePackage(\TYPO3\Flow\Package\PackageInterface $package) {
475 $packageKey = $package->getPackageKey();
476 if (!isset($this->packages[$packageKey])) {
477 $this->packages[$packageKey] = $package;
478 $this->buildPackageNamespaceAndClassesPath($package);
479 $this->sortPackageNamespaces();
480 $this->loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache(array($package));
481 }
482 return $this;
483 }
484
485 /**
486 * Builds the package namespaces and classes paths for the given packages
487 *
488 * @return void
489 */
490 protected function buildPackageNamespacesAndClassesPaths() {
491 $didLock = $this->acquireLock();
492
493 // Take a look again, after lock is acquired
494 if (!$this->loadPackageNamespacesFromCache()) {
495 foreach ($this->packages as $package) {
496 $this->buildPackageNamespaceAndClassesPath($package);
497 }
498 $this->sortPackageNamespaces();
499 $this->savePackageNamespacesAndClassesPathsToCache();
500 // The class alias map has to be rebuilt first, because ext_autoload files can contain
501 // old class names that need established class aliases.
502 $classNameToAliasMapping = $this->classAliasMap->setPackages($this->packages)->buildMappingAndInitializeEarlyInstanceMapping();
503 $this->loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache($this->packages);
504 $this->classAliasMap->buildMappingFiles($classNameToAliasMapping);
505 $this->transferRuntimeClassInformationCacheEntriesToClassesCache();
506 }
507
508 $this->releaseLock($didLock);
509 }
510
511 /**
512 * Builds the namespace and class paths for a single package
513 *
514 * @param \TYPO3\Flow\Package\PackageInterface $package
515 * @return void
516 */
517 protected function buildPackageNamespaceAndClassesPath(\TYPO3\Flow\Package\PackageInterface $package) {
518 if ($package instanceof \TYPO3\Flow\Package\PackageInterface) {
519 $this->buildPackageNamespace($package);
520 }
521 if ($package instanceof PackageInterface) {
522 $this->buildPackageClassPathsForLegacyExtension($package);
523 }
524 }
525
526 /**
527 * Load package namespaces from cache
528 *
529 * @return bool TRUE if package namespaces were loaded
530 */
531 protected function loadPackageNamespacesFromCache() {
532 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
533 if ($cacheEntryIdentifier === NULL) {
534 return FALSE;
535 }
536 $packageData = $this->coreCache->requireOnce($cacheEntryIdentifier);
537 if ($packageData !== FALSE) {
538 list($packageNamespaces, $packageClassesPaths) = $packageData;
539 if (is_array($packageNamespaces) && is_array($packageClassesPaths)) {
540 $this->packageNamespaces = $packageNamespaces;
541 $this->packageClassesPaths = $packageClassesPaths;
542 return TRUE;
543 }
544 }
545 return FALSE;
546 }
547
548 /**
549 * Extracts the namespace from a package
550 *
551 * @param \TYPO3\Flow\Package\PackageInterface $package
552 * @return void
553 */
554 protected function buildPackageNamespace(\TYPO3\Flow\Package\PackageInterface $package) {
555 $packageNamespace = $package->getNamespace();
556 // Ignore legacy extensions with unkown vendor name
557 if ($packageNamespace[0] !== '*') {
558 $this->packageNamespaces[$packageNamespace] = array(
559 'namespaceLength' => strlen($packageNamespace),
560 'classesPath' => $package->getClassesPath(),
561 'packagePath' => $package->getPackagePath(),
562 'substituteNamespaceInPath' => ($package instanceof PackageInterface)
563 );
564 }
565 }
566
567 /**
568 * Save autoload registry to cache
569 *
570 * @param array $packages
571 * @return void
572 */
573 protected function loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache(array $packages) {
574 $classFileAutoloadRegistry = array();
575 foreach ($packages as $package) {
576 if ($package instanceof PackageInterface) {
577 $classFilesFromAutoloadRegistry = $package->getClassFilesFromAutoloadRegistry();
578 if (is_array($classFilesFromAutoloadRegistry)) {
579 $classFileAutoloadRegistry = array_merge($classFileAutoloadRegistry, $classFilesFromAutoloadRegistry);
580 }
581 }
582 }
583 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
584 $lowercasedClassName = strtolower($className);
585 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName]) && @file_exists($classFilePath)) {
586 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
587 }
588 }
589 }
590
591 /**
592 * Transfers all entries from the early class information cache to
593 * the classes cache in order to make them persistent
594 *
595 * @return void
596 */
597 protected function transferRuntimeClassInformationCacheEntriesToClassesCache() {
598 foreach ($this->runtimeClassLoadingInformationCache as $classLoadingInformation) {
599 $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $classLoadingInformation[1]));
600 if (!$this->classesCache->has($cacheEntryIdentifier)) {
601 $this->classesCache->set($cacheEntryIdentifier, implode("\xff", $classLoadingInformation));
602 }
603 }
604 }
605
606 /**
607 * @param PackageInterface $package
608 * @return void
609 */
610 protected function buildPackageClassPathsForLegacyExtension(PackageInterface $package) {
611 $this->packageClassesPaths[$package->getPackageKey()] = $package->getClassesPath();
612 foreach (array_keys($package->getPackageReplacementKeys()) as $packageToReplace) {
613 $this->packageClassesPaths[$packageToReplace] = $package->getClassesPath();
614 }
615 }
616
617 /**
618 * Save package namespaces and classes paths to cache
619 *
620 * @return void
621 */
622 protected function savePackageNamespacesAndClassesPathsToCache() {
623 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
624 if ($cacheEntryIdentifier !== NULL) {
625 $this->coreCache->set(
626 $this->getCacheEntryIdentifier(),
627 'return ' . var_export(array($this->packageNamespaces, $this->packageClassesPaths), TRUE) . ';'
628 );
629 }
630 }
631
632 /**
633 * Sorts longer package namespaces first, to find specific matches before generic ones
634 *
635 * @return void
636 */
637 protected function sortPackageNamespaces() {
638 $sortPackages = function ($a, $b) {
639 if (($lenA = strlen($a)) === ($lenB = strlen($b))) {
640 return strcmp($a, $b);
641 }
642 return $lenA > $lenB ? -1 : 1;
643 };
644 uksort($this->packageNamespaces, $sortPackages);
645 }
646
647 /**
648 * This method is necessary for the early loading of the cores autoload registry
649 *
650 * @param array $classFileAutoloadRegistry
651 * @return void
652 */
653 public function setRuntimeClassLoadingInformationFromAutoloadRegistry(array $classFileAutoloadRegistry) {
654 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
655 $lowercasedClassName = strtolower($className);
656 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName])) {
657 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
658 }
659 }
660 }
661
662 /**
663 * Set alias for class name
664 *
665 * @param string $aliasClassName
666 * @param string $originalClassName
667 * @return bool
668 */
669 public function setAliasForClassName($aliasClassName, $originalClassName) {
670 return $this->classAliasMap->setAliasForClassName($aliasClassName, $originalClassName);
671 }
672
673 /**
674 * Get class name for alias
675 *
676 * @param string $alias
677 * @return mixed
678 */
679 static public function getClassNameForAlias($alias) {
680 return static::$staticAliasMap->getClassNameForAlias($alias);
681 }
682
683 /**
684 * Get alias for class name
685 *
686 * @param string $className
687 * @return mixed
688 * @deprecated since 6.2, will be removed 2 versions later - use getAliasesForClassName() instead
689 */
690 static public function getAliasForClassName($className) {
691 GeneralUtility::logDeprecatedFunction();
692 $aliases = static::$staticAliasMap->getAliasesForClassName($className);
693 return is_array($aliases) && isset($aliases[0]) ? $aliases[0] : NULL;
694 }
695
696 /**
697 * Get an aliases for a class name
698 *
699 * @param string $className
700 * @return mixed
701 */
702 static public function getAliasesForClassName($className) {
703 return static::$staticAliasMap->getAliasesForClassName($className);
704 }
705
706 /**
707 * Acquires a lock for the cache if we didn't already lock before.
708 *
709 * @return bool TRUE if the cache was acquired by this call and needs to be released
710 * @throws \RuntimeException
711 */
712 protected function acquireLock() {
713 if (!$this->isLoadingLocker) {
714 $lockObject = $this->getLocker();
715
716 if ($lockObject === NULL) {
717 // During installation typo3temp does not yet exist, so the locker can not
718 // do its job. In this case it does not need to be released again.
719 return FALSE;
720 }
721
722 // We didn't lock yet so do it
723 if (!$lockObject->getLockStatus()) {
724 if (!$lockObject->acquireExclusiveLock()) {
725 throw new \RuntimeException('Could not acquire lock for ClassLoader cache creation.', 1394480725);
726 }
727 return TRUE;
728 }
729 }
730 return FALSE;
731 }
732
733 /**
734 * Releases a lock
735 *
736 * @param bool $needRelease The result of the call to acquireLock()
737 *
738 * @return void
739 */
740 protected function releaseLock($needRelease) {
741 if ($needRelease) {
742 $lockObject = $this->getLocker();
743 $lockObject->release();
744 }
745 }
746
747 /**
748 * Gets the TYPO3 Locker object or creates an instance of it.
749 *
750 * @throws \RuntimeException
751 * @return \TYPO3\CMS\Core\Locking\Locker|NULL Only NULL if we are in installer and typo3temp does not exist yet
752 */
753 protected function getLocker() {
754 if (NULL === $this->lockObject) {
755 $this->isLoadingLocker = TRUE;
756
757 try {
758 $this->lockObject = new Locker('ClassLoader-cache-classes', Locker::LOCKING_METHOD_SIMPLE);
759 } catch (\RuntimeException $e) {
760 // The RuntimeException in constructor happens if directory typo3temp/locks could not be created.
761 // This usually happens during installation step 1 where typo3temp itself does not exist yet. In
762 // this case we proceed without locking, otherwise a missing typo3temp directory indicates a
763 // hard problem of the instance and we throw up.
764 // @TODO: This solution currently conflicts with separation of concerns since the class loader
765 // handles installation specific stuff. Find a better way to do this.
766 if (defined('TYPO3_enterInstallScript') && TYPO3_enterInstallScript) {
767 // Installer is running => So work without Locking.
768 return NULL;
769 } else {
770 throw $e;
771 }
772 }
773 $this->lockObject->setEnableLogging(FALSE);
774 $this->isLoadingLocker = FALSE;
775 }
776
777 return $this->lockObject;
778 }
779 }