bef37cc9099e2acaa9e2c6a44b286bb1502f7275
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Core / ClassLoadingInformation.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 Composer\Autoload\ClassLoader;
18 use TYPO3\ClassAliasLoader\ClassAliasMap;
19 use TYPO3\CMS\Core\Package\PackageInterface;
20 use TYPO3\CMS\Core\Package\PackageManager;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Get and manipulate class loading information, only necessary/in use
25 * when TYPO3 is not purely set up by composer but when e.g. extensions are installed via the extension manager
26 * by utilizing the composer class loader and adding more information built by the ClassLoadingInformationGenerator
27 * class.
28 *
29 * @internal
30 */
31 class ClassLoadingInformation
32 {
33 /**
34 * Base directory storing all autoload information
35 */
36 const AUTOLOAD_INFO_DIR = 'typo3temp/var/autoload/';
37
38 /**
39 * Base directory storing all autoload information in testing context
40 */
41 const AUTOLOAD_INFO_DIR_TESTS = 'typo3temp/var/autoload-tests/';
42
43 /**
44 * Name of file that contains all classes-filename mappings
45 */
46 const AUTOLOAD_CLASSMAP_FILENAME = 'autoload_classmap.php';
47
48 /**
49 * Name of file that contains all PSR4 mappings, fetched from the composer.json files of extensions
50 */
51 const AUTOLOAD_PSR4_FILENAME = 'autoload_psr4.php';
52
53 /**
54 * Name of file that contains all class alias mappings
55 */
56 const AUTOLOAD_CLASSALIASMAP_FILENAME = 'autoload_classaliasmap.php';
57
58 /**
59 * Checks if the autoload_classmap.php exists and we are not in testing context.
60 * Used to see if the ClassLoadingInformationGenerator should be called.
61 *
62 * @return bool
63 */
64 public static function isClassLoadingInformationAvailable()
65 {
66 return !self::isTestingContext() && file_exists(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME);
67 }
68
69 /**
70 * Puts all information compiled by the ClassLoadingInformationGenerator to files
71 */
72 public static function dumpClassLoadingInformation()
73 {
74 self::ensureAutoloadInfoDirExists();
75 $composerClassLoader = static::getClassLoader();
76 $activeExtensionPackages = static::getActiveExtensionPackages();
77
78 /** @var ClassLoadingInformationGenerator $generator */
79 $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
80 $classInfoFiles = $generator->buildAutoloadInformationFiles();
81 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME, $classInfoFiles['classMapFile']);
82 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME, $classInfoFiles['psr-4File']);
83
84 $classAliasMapFile = $generator->buildClassAliasMapFile();
85 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSALIASMAP_FILENAME, $classAliasMapFile);
86 }
87
88 /**
89 * Registers the class aliases, the class maps and the PSR4 prefixes previously identified by
90 * the ClassLoadingInformationGenerator during runtime.
91 */
92 public static function registerClassLoadingInformation()
93 {
94 $composerClassLoader = static::getClassLoader();
95
96 $dynamicClassAliasMapFile = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSALIASMAP_FILENAME;
97 if (file_exists($dynamicClassAliasMapFile)) {
98 $classAliasMap = require $dynamicClassAliasMapFile;
99 if (is_array($classAliasMap) && !empty($classAliasMap['aliasToClassNameMapping']) && !empty($classAliasMap['classNameToAliasMapping'])) {
100 ClassAliasMap::addAliasMap($classAliasMap);
101 }
102 }
103
104 $dynamicClassMapFile = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME;
105 if (file_exists($dynamicClassMapFile)) {
106 $classMap = require $dynamicClassMapFile;
107 if (!empty($classMap) && is_array($classMap)) {
108 $composerClassLoader->addClassMap($classMap);
109 }
110 }
111
112 $dynamicPsr4File = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME;
113 if (file_exists($dynamicPsr4File)) {
114 $psr4 = require $dynamicPsr4File;
115 if (is_array($psr4)) {
116 foreach ($psr4 as $prefix => $paths) {
117 $composerClassLoader->setPsr4($prefix, $paths);
118 }
119 }
120 }
121 }
122
123 /**
124 * Sets class loading information for a package for the current web request
125 *
126 * @param PackageInterface $package
127 * @throws \TYPO3\CMS\Core\Error\Exception
128 */
129 public static function registerTransientClassLoadingInformationForPackage(PackageInterface $package)
130 {
131 $composerClassLoader = static::getClassLoader();
132 $activeExtensionPackages = static::getActiveExtensionPackages();
133
134 /** @var ClassLoadingInformationGenerator $generator */
135 $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
136
137 $classInformation = $generator->buildClassLoadingInformationForPackage($package);
138 $composerClassLoader->addClassMap($classInformation['classMap']);
139 foreach ($classInformation['psr-4'] as $prefix => $paths) {
140 $composerClassLoader->setPsr4($prefix, $paths);
141 }
142 $classAliasMap = $generator->buildClassAliasMapForPackage($package);
143 if (is_array($classAliasMap) && !empty($classAliasMap['aliasToClassNameMapping']) && !empty($classAliasMap['classNameToAliasMapping'])) {
144 ClassAliasMap::addAliasMap($classAliasMap);
145 }
146 }
147
148 /**
149 * @return string
150 */
151 protected static function getClassLoadingInformationDirectory()
152 {
153 if (self::isTestingContext()) {
154 return PATH_site . self::AUTOLOAD_INFO_DIR_TESTS;
155 } else {
156 return PATH_site . self::AUTOLOAD_INFO_DIR;
157 }
158 }
159
160 /**
161 * Get class name for alias
162 *
163 * @param string $alias
164 * @return mixed
165 */
166 public static function getClassNameForAlias($alias)
167 {
168 return ClassAliasMap::getClassNameForAlias($alias);
169 }
170
171 /**
172 * Ensures the defined path for class information files exists
173 * And clears it in case we're in testing context
174 */
175 protected static function ensureAutoloadInfoDirExists()
176 {
177 $autoloadInfoDir = self::getClassLoadingInformationDirectory();
178 if (!file_exists($autoloadInfoDir)) {
179 GeneralUtility::mkdir_deep($autoloadInfoDir);
180 }
181 }
182
183 /**
184 * Internal method calling the bootstrap to fetch the composer class loader
185 *
186 * @return ClassLoader
187 * @throws \TYPO3\CMS\Core\Exception
188 */
189 protected static function getClassLoader()
190 {
191 return Bootstrap::getInstance()->getEarlyInstance(ClassLoader::class);
192 }
193
194 /**
195 * Internal method calling the bootstrap to get application context information
196 *
197 * @return bool
198 * @throws \TYPO3\CMS\Core\Exception
199 */
200 protected static function isTestingContext()
201 {
202 return Bootstrap::getInstance()->getApplicationContext()->isTesting();
203 }
204
205 /**
206 * Get all packages except the protected ones, as they are covered already
207 *
208 * @return PackageInterface[]
209 */
210 protected static function getActiveExtensionPackages()
211 {
212 $activeExtensionPackages = [];
213 /** @var PackageManager $packageManager */
214 $packageManager = Bootstrap::getInstance()->getEarlyInstance(PackageManager::class);
215 foreach ($packageManager->getActivePackages() as $package) {
216 if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') {
217 // Skip all core packages as the class loading info is prepared for them already
218 continue;
219 }
220 $activeExtensionPackages[] = $package;
221 }
222 return $activeExtensionPackages;
223 }
224 }