[BUGFIX] Cache missing class information only in productive context
[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 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) || (bool)require_once $classLoadingInformation[0];
183 }
184 if ($loadingSuccessful && count($classLoadingInformation) > 2) {
185 $originalClassName = $classLoadingInformation[1];
186 foreach (array_slice($classLoadingInformation, 2) as $aliasClassName) {
187 $this->setAliasForClassName($aliasClassName, $originalClassName);
188 }
189 }
190
191 return $loadingSuccessful;
192 }
193
194 /**
195 * Get class loading information for the given identifier for cache
196 * Return values:
197 * - array with class information (empty if the class is invalid)
198 * - FALSE if no class information is found in cache (cache miss)
199 * - NULL if the cache identifier is invalid (cache failure)
200 *
201 * @param string $cacheEntryIdentifier The identifier to fetch entry from cache
202 * @return array|FALSE The class information, empty array if class is unkown or FALSE if class information was not found in cache.
203 */
204 public function getClassLoadingInformationFromCache($cacheEntryIdentifier) {
205 $rawClassLoadingInformation = $this->classesCache->get($cacheEntryIdentifier);
206
207 if ($rawClassLoadingInformation === '') {
208 return array();
209 }
210
211 if ($rawClassLoadingInformation) {
212 return explode("\xff", $rawClassLoadingInformation);
213 }
214 return FALSE;
215 }
216
217 /**
218 * Builds the class loading information and writes it to the cache. It handles Locking for this cache.
219 *
220 * Caution: The function loadClass can be called "recursively" by spl_autoloader. This needs to be observed when
221 * locking for cache access. Only the first call to loadClass may acquire and release the lock!
222 *
223 * @param string $cacheEntryIdentifier Cache identifier for this class
224 * @param string $className Name of class this information is for
225 *
226 * @return array|FALSE The class information, empty array if class is unkown or FALSE if class information was not found in cache.
227 */
228 protected function buildCachedClassLoadingInformation($cacheEntryIdentifier, $className) {
229 // We do not need locking if we are in earlyCache mode
230 $didLock = FALSE;
231 if (!$this->isEarlyCache) {
232 $didLock = $this->acquireLock();
233 }
234
235 // Look again into the cache after we got the lock, data might have been generated meanwhile
236 $classLoadingInformation = $this->getClassLoadingInformationFromCache($cacheEntryIdentifier);
237 // Handle repeated cache miss
238 if ($classLoadingInformation === FALSE) {
239 // Generate class information
240 $classLoadingInformation = $this->buildClassLoadingInformation($className);
241
242 if ($classLoadingInformation !== FALSE) {
243 // If we found class information, cache it
244 $this->classesCache->set(
245 $cacheEntryIdentifier,
246 implode("\xff", $classLoadingInformation),
247 $this->isEarlyCache ? array('early') : array()
248 );
249 } elseif (!$this->isEarlyCache) {
250 if ($this->context->isProduction()) {
251 // Cache that the class is unknown
252 $this->classesCache->set($cacheEntryIdentifier, '');
253 }
254 }
255 }
256
257 $this->releaseLock($didLock);
258
259 return $classLoadingInformation;
260 }
261
262 /**
263 * Builds the class loading information
264 *
265 * @param string $className Name of class this information is for
266 *
267 * @return array|FALSE The class information or FALSE if class was not found
268 */
269 public function buildClassLoadingInformation($className) {
270 $classLoadingInformation = $this->buildClassLoadingInformationForClassFromCorePackage($className);
271
272 if ($classLoadingInformation === FALSE) {
273 $classLoadingInformation = $this->fetchClassLoadingInformationFromRuntimeCache($className);
274 }
275
276 if ($classLoadingInformation === FALSE) {
277 $classLoadingInformation = $this->buildClassLoadingInformationForClassFromRegisteredPackages($className);
278 }
279
280 if ($classLoadingInformation === FALSE) {
281 $classLoadingInformation = $this->buildClassLoadingInformationForClassByNamingConvention($className);
282 }
283
284 return $classLoadingInformation;
285 }
286
287 /**
288 * Find out if a class name is valid
289 *
290 * @param string $className
291 * @return bool
292 */
293 protected function isValidClassName($className) {
294 return (bool)preg_match(self::VALID_CLASSNAME_PATTERN, $className);
295 }
296
297 /**
298 * Retrieve class loading information for class from core package
299 *
300 * @param string $className
301 * @return array|FALSE
302 */
303 protected function buildClassLoadingInformationForClassFromCorePackage($className) {
304 if (substr($className, 0, 14) === 'TYPO3\\CMS\\Core') {
305 $classesFolder = substr($className, 15, 5) === 'Tests' ? '' : 'Classes/';
306 $classFilePath = PATH_typo3 . 'sysext/core/' . $classesFolder . str_replace('\\', '/', substr($className, 15)) . '.php';
307 if (@file_exists($classFilePath)) {
308 return array($classFilePath, $className);
309 }
310 }
311 return FALSE;
312 }
313
314 /**
315 * Retrieve class loading information from early class name autoload registry cache
316 *
317 * @param string $className
318 * @return array|FALSE
319 */
320 protected function fetchClassLoadingInformationFromRuntimeCache($className) {
321 $lowercasedClassName = strtolower($className);
322 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName])) {
323 return FALSE;
324 }
325 $classInformation = $this->runtimeClassLoadingInformationCache[$lowercasedClassName];
326 return @file_exists($classInformation[0]) ? $classInformation : FALSE;
327 }
328
329 /**
330 * Retrieve class loading information from registered packages
331 *
332 * @param string $className
333 * @return array|FALSE
334 */
335 protected function buildClassLoadingInformationForClassFromRegisteredPackages($className) {;
336 foreach ($this->packageNamespaces as $packageNamespace => $packageData) {
337 if (substr(str_replace('_', '\\', $className), 0, $packageData['namespaceLength']) === $packageNamespace) {
338 if ($packageData['substituteNamespaceInPath']) {
339 // If it's a TYPO3 package, classes don't comply to PSR-0.
340 // The namespace part is substituted.
341 $classPathAndFilename = '/' . str_replace('\\', '/', ltrim(substr($className, $packageData['namespaceLength']), '\\')) . '.php';
342 } else {
343 // Make the classname PSR-0 compliant by replacing underscores only in the classname not in the namespace
344 $classPathAndFilename = '';
345 $lastNamespacePosition = strrpos($className, '\\');
346 if ($lastNamespacePosition !== FALSE) {
347 $namespace = substr($className, 0, $lastNamespacePosition);
348 $className = substr($className, $lastNamespacePosition + 1);
349 $classPathAndFilename = str_replace('\\', '/', $namespace) . '/';
350 }
351 $classPathAndFilename .= str_replace('_', '/', $className) . '.php';
352 }
353 if (strtolower(substr($className, $packageData['namespaceLength'], 5)) === 'tests') {
354 $classPathAndFilename = $packageData['packagePath'] . $classPathAndFilename;
355 } else {
356 $classPathAndFilename = $packageData['classesPath'] . $classPathAndFilename;
357 }
358 if (@file_exists($classPathAndFilename)) {
359 return array($classPathAndFilename, $className);
360 }
361 }
362 }
363 return FALSE;
364 }
365
366 /**
367 * Retrieve class loading information based on 'extbase' naming convention into the registry.
368 *
369 * @param string $className Class name to find source file of
370 * @return array|FALSE
371 */
372 protected function buildClassLoadingInformationForClassByNamingConvention($className) {
373 $delimiter = '_';
374 // To handle namespaced class names, split the class name at the
375 // namespace delimiters.
376 if (strpos($className, '\\') !== FALSE) {
377 $delimiter = '\\';
378 }
379
380 $classNameParts = explode($delimiter, $className, 4);
381
382 // We only handle classes that follow the convention Vendor\Product\Classname or is longer
383 // so we won't deal with class names that only have one or two parts
384 if (count($classNameParts) <= 2) {
385 return FALSE;
386 }
387
388 if (
389 isset($classNameParts[0])
390 && isset($classNameParts[1])
391 && $classNameParts[0] === 'TYPO3'
392 && $classNameParts[1] === 'CMS'
393 ) {
394 $extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($classNameParts[2]);
395 $classNameWithoutVendorAndProduct = $classNameParts[3];
396 } else {
397 $extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($classNameParts[1]);
398 $classNameWithoutVendorAndProduct = $classNameParts[2];
399
400 if (isset($classNameParts[3])) {
401 $classNameWithoutVendorAndProduct .= $delimiter . $classNameParts[3];
402 }
403 }
404
405 if ($extensionKey && isset($this->packageClassesPaths[$extensionKey])) {
406 if (substr(strtolower($classNameWithoutVendorAndProduct), 0, 5) === 'tests') {
407 $classesPath = $this->packages[$extensionKey]->getPackagePath();
408 } else {
409 $classesPath = $this->packageClassesPaths[$extensionKey];
410 }
411 $classFilePath = $classesPath . strtr($classNameWithoutVendorAndProduct, $delimiter, '/') . '.php';
412 if (@file_exists($classFilePath)) {
413 return array($classFilePath, $className);
414 }
415 }
416
417 return FALSE;
418 }
419
420 /**
421 * Get cache entry identifier for the package namespaces cache
422 *
423 * @return string|NULL identifier
424 */
425 protected function getCacheEntryIdentifier() {
426 return $this->cacheIdentifier !== NULL
427 ? 'ClassLoader_' . $this->cacheIdentifier
428 : NULL;
429 }
430
431 /**
432 * Set cache identifier
433 *
434 * @param string $cacheIdentifier Cache identifier for package namespaces cache
435 * @return ClassLoader
436 */
437 public function setCacheIdentifier($cacheIdentifier) {
438 $this->cacheIdentifier = $cacheIdentifier;
439 return $this;
440 }
441
442 /**
443 * Sets the available packages
444 *
445 * @param array $packages An array of \TYPO3\Flow\Package\Package objects
446 * @return ClassLoader
447 */
448 public function setPackages(array $packages) {
449 $this->packages = $packages;
450
451 if (!$this->loadPackageNamespacesFromCache()) {
452 $this->buildPackageNamespacesAndClassesPaths();
453 } else {
454 $this->classAliasMap->setPackages($packages);
455 }
456 // Clear the runtime cache for runtime activated packages
457 $this->runtimeClassLoadingInformationCache = array();
458 return $this;
459 }
460
461 /**
462 * Add a package to class loader just during runtime, so classes can be loaded without the need for a new request
463 *
464 * @param \TYPO3\Flow\Package\PackageInterface $package
465 * @return ClassLoader
466 */
467 public function addActivePackage(\TYPO3\Flow\Package\PackageInterface $package) {
468 $packageKey = $package->getPackageKey();
469 if (!isset($this->packages[$packageKey])) {
470 $this->packages[$packageKey] = $package;
471 $this->buildPackageNamespaceAndClassesPath($package);
472 $this->sortPackageNamespaces();
473 $this->loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache(array($package));
474 }
475 return $this;
476 }
477
478 /**
479 * Builds the package namespaces and classes paths for the given packages
480 *
481 * @return void
482 */
483 protected function buildPackageNamespacesAndClassesPaths() {
484 $didLock = $this->acquireLock();
485
486 // Take a look again, after lock is acquired
487 if (!$this->loadPackageNamespacesFromCache()) {
488 foreach ($this->packages as $package) {
489 $this->buildPackageNamespaceAndClassesPath($package);
490 }
491 $this->sortPackageNamespaces();
492 $this->savePackageNamespacesAndClassesPathsToCache();
493 // The class alias map has to be rebuilt first, because ext_autoload files can contain
494 // old class names that need established class aliases.
495 $classNameToAliasMapping = $this->classAliasMap->setPackages($this->packages)->buildMappingAndInitializeEarlyInstanceMapping();
496 $this->loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache($this->packages);
497 $this->classAliasMap->buildMappingFiles($classNameToAliasMapping);
498 $this->transferRuntimeClassInformationCacheEntriesToClassesCache();
499 }
500
501 $this->releaseLock($didLock);
502 }
503
504 /**
505 * Builds the namespace and class paths for a single package
506 *
507 * @param \TYPO3\Flow\Package\PackageInterface $package
508 * @return void
509 */
510 protected function buildPackageNamespaceAndClassesPath(\TYPO3\Flow\Package\PackageInterface $package) {
511 if ($package instanceof \TYPO3\Flow\Package\PackageInterface) {
512 $this->buildPackageNamespace($package);
513 }
514 if ($package instanceof PackageInterface) {
515 $this->buildPackageClassPathsForLegacyExtension($package);
516 }
517 }
518
519 /**
520 * Load package namespaces from cache
521 *
522 * @return bool TRUE if package namespaces were loaded
523 */
524 protected function loadPackageNamespacesFromCache() {
525 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
526 if ($cacheEntryIdentifier === NULL) {
527 return FALSE;
528 }
529 $packageData = $this->coreCache->requireOnce($cacheEntryIdentifier);
530 if ($packageData !== FALSE) {
531 list($packageNamespaces, $packageClassesPaths) = $packageData;
532 if (is_array($packageNamespaces) && is_array($packageClassesPaths)) {
533 $this->packageNamespaces = $packageNamespaces;
534 $this->packageClassesPaths = $packageClassesPaths;
535 return TRUE;
536 }
537 }
538 return FALSE;
539 }
540
541 /**
542 * Extracts the namespace from a package
543 *
544 * @param \TYPO3\Flow\Package\PackageInterface $package
545 * @return void
546 */
547 protected function buildPackageNamespace(\TYPO3\Flow\Package\PackageInterface $package) {
548 $packageNamespace = $package->getNamespace();
549 // Ignore legacy extensions with unkown vendor name
550 if ($packageNamespace[0] !== '*') {
551 $this->packageNamespaces[$packageNamespace] = array(
552 'namespaceLength' => strlen($packageNamespace),
553 'classesPath' => $package->getClassesPath(),
554 'packagePath' => $package->getPackagePath(),
555 'substituteNamespaceInPath' => ($package instanceof PackageInterface)
556 );
557 }
558 }
559
560 /**
561 * Save autoload registry to cache
562 *
563 * @param array $packages
564 * @return void
565 */
566 protected function loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache(array $packages) {
567 $classFileAutoloadRegistry = array();
568 foreach ($packages as $package) {
569 if ($package instanceof PackageInterface) {
570 $classFilesFromAutoloadRegistry = $package->getClassFilesFromAutoloadRegistry();
571 if (is_array($classFilesFromAutoloadRegistry)) {
572 $classFileAutoloadRegistry = array_merge($classFileAutoloadRegistry, $classFilesFromAutoloadRegistry);
573 }
574 }
575 }
576 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
577 $lowercasedClassName = strtolower($className);
578 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName]) && @file_exists($classFilePath)) {
579 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
580 }
581 }
582 }
583
584 /**
585 * Transfers all entries from the early class information cache to
586 * the classes cache in order to make them persistent
587 *
588 * @return void
589 */
590 protected function transferRuntimeClassInformationCacheEntriesToClassesCache() {
591 foreach ($this->runtimeClassLoadingInformationCache as $classLoadingInformation) {
592 $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $classLoadingInformation[1]));
593 if (!$this->classesCache->has($cacheEntryIdentifier)) {
594 $this->classesCache->set($cacheEntryIdentifier, implode("\xff", $classLoadingInformation));
595 }
596 }
597 }
598
599 /**
600 * @param PackageInterface $package
601 * @return void
602 */
603 protected function buildPackageClassPathsForLegacyExtension(PackageInterface $package) {
604 $this->packageClassesPaths[$package->getPackageKey()] = $package->getClassesPath();
605 foreach (array_keys($package->getPackageReplacementKeys()) as $packageToReplace) {
606 $this->packageClassesPaths[$packageToReplace] = $package->getClassesPath();
607 }
608 }
609
610 /**
611 * Save package namespaces and classes paths to cache
612 *
613 * @return void
614 */
615 protected function savePackageNamespacesAndClassesPathsToCache() {
616 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
617 if ($cacheEntryIdentifier !== NULL) {
618 $this->coreCache->set(
619 $this->getCacheEntryIdentifier(),
620 'return ' . var_export(array($this->packageNamespaces, $this->packageClassesPaths), TRUE) . ';'
621 );
622 }
623 }
624
625 /**
626 * Sorts longer package namespaces first, to find specific matches before generic ones
627 *
628 * @return void
629 */
630 protected function sortPackageNamespaces() {
631 $sortPackages = function ($a, $b) {
632 if (($lenA = strlen($a)) === ($lenB = strlen($b))) {
633 return strcmp($a, $b);
634 }
635 return $lenA > $lenB ? -1 : 1;
636 };
637 uksort($this->packageNamespaces, $sortPackages);
638 }
639
640 /**
641 * This method is necessary for the early loading of the cores autoload registry
642 *
643 * @param array $classFileAutoloadRegistry
644 * @return void
645 */
646 public function setRuntimeClassLoadingInformationFromAutoloadRegistry(array $classFileAutoloadRegistry) {
647 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
648 $lowercasedClassName = strtolower($className);
649 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName])) {
650 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
651 }
652 }
653 }
654
655 /**
656 * Set alias for class name
657 *
658 * @param string $aliasClassName
659 * @param string $originalClassName
660 * @return bool
661 */
662 public function setAliasForClassName($aliasClassName, $originalClassName) {
663 return $this->classAliasMap->setAliasForClassName($aliasClassName, $originalClassName);
664 }
665
666 /**
667 * Get class name for alias
668 *
669 * @param string $alias
670 * @return mixed
671 */
672 static public function getClassNameForAlias($alias) {
673 return static::$staticAliasMap->getClassNameForAlias($alias);
674 }
675
676 /**
677 * Get alias for class name
678 *
679 * @param string $className
680 * @return mixed
681 * @deprecated since 6.2, will be removed 2 versions later - use getAliasesForClassName() instead
682 */
683 static public function getAliasForClassName($className) {
684 GeneralUtility::logDeprecatedFunction();
685 $aliases = static::$staticAliasMap->getAliasesForClassName($className);
686 return is_array($aliases) && isset($aliases[0]) ? $aliases[0] : NULL;
687 }
688
689 /**
690 * Get an aliases for a class name
691 *
692 * @param string $className
693 * @return mixed
694 */
695 static public function getAliasesForClassName($className) {
696 return static::$staticAliasMap->getAliasesForClassName($className);
697 }
698
699 /**
700 * Acquires a lock for the cache if we didn't already lock before.
701 *
702 * @return bool TRUE if the cache was acquired by this call and needs to be released
703 * @throws \RuntimeException
704 */
705 protected function acquireLock() {
706 if (!$this->isLoadingLocker) {
707 $lockObject = $this->getLocker();
708
709 if ($lockObject === NULL) {
710 // During installation typo3temp does not yet exist, so the locker can not
711 // do its job. In this case it does not need to be released again.
712 return FALSE;
713 }
714
715 // We didn't lock yet so do it
716 if (!$lockObject->getLockStatus()) {
717 if (!$lockObject->acquireExclusiveLock()) {
718 throw new \RuntimeException('Could not acquire lock for ClassLoader cache creation.', 1394480725);
719 }
720 return TRUE;
721 }
722 }
723 return FALSE;
724 }
725
726 /**
727 * Releases a lock
728 *
729 * @param bool $needRelease The result of the call to acquireLock()
730 *
731 * @return void
732 */
733 protected function releaseLock($needRelease) {
734 if ($needRelease) {
735 $lockObject = $this->getLocker();
736 $lockObject->release();
737 }
738 }
739
740 /**
741 * Gets the TYPO3 Locker object or creates an instance of it.
742 *
743 * @throws \RuntimeException
744 * @return \TYPO3\CMS\Core\Locking\Locker|NULL Only NULL if we are in installer and typo3temp does not exist yet
745 */
746 protected function getLocker() {
747 if (NULL === $this->lockObject) {
748 $this->isLoadingLocker = TRUE;
749
750 try {
751 $this->lockObject = new Locker('ClassLoader-cache-classes', Locker::LOCKING_METHOD_SIMPLE);
752 } catch (\RuntimeException $e) {
753 // The RuntimeException in constructor happens if directory typo3temp/locks could not be created.
754 // This usually happens during installation step 1 where typo3temp itself does not exist yet. In
755 // this case we proceed without locking, otherwise a missing typo3temp directory indicates a
756 // hard problem of the instance and we throw up.
757 // @TODO: This solution currently conflicts with separation of concerns since the class loader
758 // handles installation specific stuff. Find a better way to do this.
759 if (defined('TYPO3_enterInstallScript') && TYPO3_enterInstallScript) {
760 // Installer is running => So work without Locking.
761 return NULL;
762 } else {
763 throw $e;
764 }
765 }
766 $this->lockObject->setEnableLogging(FALSE);
767 $this->isLoadingLocker = FALSE;
768 }
769
770 return $this->lockObject;
771 }
772 }