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