868835d63266ae492fb9cf4fb5bf20d0a8400f0b
[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 foreach ($this->packages as $package) {
491 $this->buildPackageNamespaceAndClassesPath($package);
492 }
493 $this->sortPackageNamespaces();
494 $this->savePackageNamespacesAndClassesPathsToCache();
495 // The class alias map has to be rebuilt first, because ext_autoload files can contain
496 // old class names that need established class aliases.
497 $classNameToAliasMapping = $this->classAliasMap->setPackages($this->packages)->buildMappingAndInitializeEarlyInstanceMapping();
498 $this->loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache($this->packages);
499 $this->classAliasMap->buildMappingFiles($classNameToAliasMapping);
500 $this->transferRuntimeClassInformationCacheEntriesToClassesCache();
501 }
502
503 $this->releaseLock($didLock);
504 }
505
506 /**
507 * Builds the namespace and class paths for a single package
508 *
509 * @param \TYPO3\Flow\Package\PackageInterface $package
510 * @return void
511 */
512 protected function buildPackageNamespaceAndClassesPath(\TYPO3\Flow\Package\PackageInterface $package) {
513 if ($package instanceof \TYPO3\Flow\Package\PackageInterface) {
514 $this->buildPackageNamespace($package);
515 }
516 if ($package instanceof PackageInterface) {
517 $this->buildPackageClassPathsForLegacyExtension($package);
518 }
519 }
520
521 /**
522 * Load package namespaces from cache
523 *
524 * @return bool TRUE if package namespaces were loaded
525 */
526 protected function loadPackageNamespacesFromCache() {
527 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
528 if ($cacheEntryIdentifier === NULL) {
529 return FALSE;
530 }
531 $packageData = $this->coreCache->requireOnce($cacheEntryIdentifier);
532 if ($packageData !== FALSE) {
533 list($packageNamespaces, $packageClassesPaths) = $packageData;
534 if (is_array($packageNamespaces) && is_array($packageClassesPaths)) {
535 $this->packageNamespaces = $packageNamespaces;
536 $this->packageClassesPaths = $packageClassesPaths;
537 return TRUE;
538 }
539 }
540 return FALSE;
541 }
542
543 /**
544 * Extracts the namespace from a package
545 *
546 * @param \TYPO3\Flow\Package\PackageInterface $package
547 * @return void
548 */
549 protected function buildPackageNamespace(\TYPO3\Flow\Package\PackageInterface $package) {
550 $packageNamespace = $package->getNamespace();
551 // Ignore legacy extensions with unkown vendor name
552 if ($packageNamespace[0] !== '*') {
553 $this->packageNamespaces[$packageNamespace] = array(
554 'namespaceLength' => strlen($packageNamespace),
555 'classesPath' => $package->getClassesPath(),
556 'packagePath' => $package->getPackagePath(),
557 'substituteNamespaceInPath' => ($package instanceof PackageInterface)
558 );
559 }
560 }
561
562 /**
563 * Save autoload registry to cache
564 *
565 * @param array $packages
566 * @return void
567 */
568 protected function loadClassFilesFromAutoloadRegistryIntoRuntimeClassInformationCache(array $packages) {
569 $classFileAutoloadRegistry = array();
570 foreach ($packages as $package) {
571 if ($package instanceof PackageInterface) {
572 $classFilesFromAutoloadRegistry = $package->getClassFilesFromAutoloadRegistry();
573 if (is_array($classFilesFromAutoloadRegistry)) {
574 $classFileAutoloadRegistry = array_merge($classFileAutoloadRegistry, $classFilesFromAutoloadRegistry);
575 }
576 }
577 }
578 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
579 $lowercasedClassName = strtolower($className);
580 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName]) && @file_exists($classFilePath)) {
581 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
582 }
583 }
584 }
585
586 /**
587 * Transfers all entries from the early class information cache to
588 * the classes cache in order to make them persistent
589 *
590 * @return void
591 */
592 protected function transferRuntimeClassInformationCacheEntriesToClassesCache() {
593 foreach ($this->runtimeClassLoadingInformationCache as $classLoadingInformation) {
594 $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $classLoadingInformation[1]));
595 if (!$this->classesCache->has($cacheEntryIdentifier)) {
596 $this->classesCache->set($cacheEntryIdentifier, implode("\xff", $classLoadingInformation));
597 }
598 }
599 }
600
601 /**
602 * @param PackageInterface $package
603 * @return void
604 */
605 protected function buildPackageClassPathsForLegacyExtension(PackageInterface $package) {
606 $this->packageClassesPaths[$package->getPackageKey()] = $package->getClassesPath();
607 foreach (array_keys($package->getPackageReplacementKeys()) as $packageToReplace) {
608 $this->packageClassesPaths[$packageToReplace] = $package->getClassesPath();
609 }
610 }
611
612 /**
613 * Save package namespaces and classes paths to cache
614 *
615 * @return void
616 */
617 protected function savePackageNamespacesAndClassesPathsToCache() {
618 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
619 if ($cacheEntryIdentifier !== NULL) {
620 $this->coreCache->set(
621 $this->getCacheEntryIdentifier(),
622 'return ' . var_export(array($this->packageNamespaces, $this->packageClassesPaths), TRUE) . ';'
623 );
624 }
625 }
626
627 /**
628 * Sorts longer package namespaces first, to find specific matches before generic ones
629 *
630 * @return void
631 */
632 protected function sortPackageNamespaces() {
633 $sortPackages = function ($a, $b) {
634 if (($lenA = strlen($a)) === ($lenB = strlen($b))) {
635 return strcmp($a, $b);
636 }
637 return $lenA > $lenB ? -1 : 1;
638 };
639 uksort($this->packageNamespaces, $sortPackages);
640 }
641
642 /**
643 * This method is necessary for the early loading of the cores autoload registry
644 *
645 * @param array $classFileAutoloadRegistry
646 * @return void
647 */
648 public function setRuntimeClassLoadingInformationFromAutoloadRegistry(array $classFileAutoloadRegistry) {
649 foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
650 $lowercasedClassName = strtolower($className);
651 if (!isset($this->runtimeClassLoadingInformationCache[$lowercasedClassName])) {
652 $this->runtimeClassLoadingInformationCache[$lowercasedClassName] = array($classFilePath, $className);
653 }
654 }
655 }
656
657 /**
658 * Set alias for class name
659 *
660 * @param string $aliasClassName
661 * @param string $originalClassName
662 * @return bool
663 */
664 public function setAliasForClassName($aliasClassName, $originalClassName) {
665 return $this->classAliasMap->setAliasForClassName($aliasClassName, $originalClassName);
666 }
667
668 /**
669 * Get class name for alias
670 *
671 * @param string $alias
672 * @return mixed
673 */
674 static public function getClassNameForAlias($alias) {
675 return static::$staticAliasMap->getClassNameForAlias($alias);
676 }
677
678 /**
679 * Get alias for class name
680 *
681 * @param string $className
682 * @return mixed
683 * @deprecated since 6.2, will be removed 2 versions later - use getAliasesForClassName() instead
684 */
685 static public function getAliasForClassName($className) {
686 GeneralUtility::logDeprecatedFunction();
687 $aliases = static::$staticAliasMap->getAliasesForClassName($className);
688 return is_array($aliases) && isset($aliases[0]) ? $aliases[0] : NULL;
689 }
690
691 /**
692 * Get an aliases for a class name
693 *
694 * @param string $className
695 * @return mixed
696 */
697 static public function getAliasesForClassName($className) {
698 return static::$staticAliasMap->getAliasesForClassName($className);
699 }
700
701 /**
702 * Acquires a lock for the cache if we didn't already lock before.
703 *
704 * @return bool TRUE if the cache was acquired by this call and needs to be released
705 * @throws \RuntimeException
706 */
707 protected function acquireLock() {
708 if (!$this->isLoadingLocker) {
709 $lockObject = $this->getLocker();
710
711 if ($lockObject === NULL) {
712 // During installation typo3temp does not yet exist, so the locker can not
713 // do its job. In this case it does not need to be released again.
714 return FALSE;
715 }
716
717 // We didn't lock yet so do it
718 if (!$lockObject->getLockStatus()) {
719 if (!$lockObject->acquireExclusiveLock()) {
720 throw new \RuntimeException('Could not acquire lock for ClassLoader cache creation.', 1394480725);
721 }
722 return TRUE;
723 }
724 }
725 return FALSE;
726 }
727
728 /**
729 * Releases a lock
730 *
731 * @param bool $needRelease The result of the call to acquireLock()
732 *
733 * @return void
734 */
735 protected function releaseLock($needRelease) {
736 if ($needRelease) {
737 $lockObject = $this->getLocker();
738 $lockObject->release();
739 }
740 }
741
742 /**
743 * Gets the TYPO3 Locker object or creates an instance of it.
744 *
745 * @throws \RuntimeException
746 * @return \TYPO3\CMS\Core\Locking\Locker|NULL Only NULL if we are in installer and typo3temp does not exist yet
747 */
748 protected function getLocker() {
749 if (NULL === $this->lockObject) {
750 $this->isLoadingLocker = TRUE;
751
752 try {
753 $this->lockObject = new Locker('ClassLoader-cache-classes', Locker::LOCKING_METHOD_SIMPLE);
754 } catch (\RuntimeException $e) {
755 // The RuntimeException in constructor happens if directory typo3temp/locks could not be created.
756 // This usually happens during installation step 1 where typo3temp itself does not exist yet. In
757 // this case we proceed without locking, otherwise a missing typo3temp directory indicates a
758 // hard problem of the instance and we throw up.
759 // @TODO: This solution currently conflicts with separation of concerns since the class loader
760 // handles installation specific stuff. Find a better way to do this.
761 if (defined('TYPO3_enterInstallScript') && TYPO3_enterInstallScript) {
762 // Installer is running => So work without Locking.
763 return NULL;
764 } else {
765 throw $e;
766 }
767 }
768 $this->lockObject->setEnableLogging(FALSE);
769 $this->isLoadingLocker = FALSE;
770 }
771
772 return $this->lockObject;
773 }
774 }