[TASK] Consistently retrieve ApplicationContext from GeneralUtility
[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 = 'typo3conf/autoload/';
37
38 /**
39 * Base directory storing all autoload information in testing context
40 */
41 const AUTOLOAD_INFO_DIR_TESTS = 'typo3conf/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 * @var ClassLoader
60 */
61 protected static $classLoader;
62
63 /**
64 * Sets the package manager instance
65 *
66 * @param ClassLoader $classLoader
67 * @internal
68 */
69 public static function setClassLoader(ClassLoader $classLoader)
70 {
71 static::$classLoader = $classLoader;
72 }
73
74 /**
75 * Checks if the autoload_classmap.php exists and we are not in testing context.
76 * Used to see if the ClassLoadingInformationGenerator should be called.
77 *
78 * @return bool
79 */
80 public static function isClassLoadingInformationAvailable()
81 {
82 return file_exists(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME);
83 }
84
85 /**
86 * Puts all information compiled by the ClassLoadingInformationGenerator to files
87 */
88 public static function dumpClassLoadingInformation()
89 {
90 self::ensureAutoloadInfoDirExists();
91 $composerClassLoader = static::getClassLoader();
92 $activeExtensionPackages = static::getActiveExtensionPackages();
93
94 /** @var ClassLoadingInformationGenerator $generator */
95 $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
96 $classInfoFiles = $generator->buildAutoloadInformationFiles();
97 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME, $classInfoFiles['classMapFile']);
98 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME, $classInfoFiles['psr-4File']);
99
100 $classAliasMapFile = $generator->buildClassAliasMapFile();
101 GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSALIASMAP_FILENAME, $classAliasMapFile);
102 }
103
104 /**
105 * Registers the class aliases, the class maps and the PSR4 prefixes previously identified by
106 * the ClassLoadingInformationGenerator during runtime.
107 */
108 public static function registerClassLoadingInformation()
109 {
110 $composerClassLoader = static::getClassLoader();
111
112 $dynamicClassAliasMapFile = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSALIASMAP_FILENAME;
113 if (file_exists($dynamicClassAliasMapFile)) {
114 $classAliasMap = require $dynamicClassAliasMapFile;
115 if (is_array($classAliasMap) && !empty($classAliasMap['aliasToClassNameMapping']) && !empty($classAliasMap['classNameToAliasMapping'])) {
116 ClassAliasMap::addAliasMap($classAliasMap);
117 }
118 }
119
120 $dynamicClassMapFile = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME;
121 if (file_exists($dynamicClassMapFile)) {
122 $classMap = require $dynamicClassMapFile;
123 if (!empty($classMap) && is_array($classMap)) {
124 $composerClassLoader->addClassMap($classMap);
125 }
126 }
127
128 $dynamicPsr4File = self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME;
129 if (file_exists($dynamicPsr4File)) {
130 $psr4 = require $dynamicPsr4File;
131 if (is_array($psr4)) {
132 foreach ($psr4 as $prefix => $paths) {
133 $composerClassLoader->setPsr4($prefix, $paths);
134 }
135 }
136 }
137 }
138
139 /**
140 * Sets class loading information for a package for the current web request
141 *
142 * @param PackageInterface $package
143 * @throws \TYPO3\CMS\Core\Error\Exception
144 */
145 public static function registerTransientClassLoadingInformationForPackage(PackageInterface $package)
146 {
147 $composerClassLoader = static::getClassLoader();
148 $activeExtensionPackages = static::getActiveExtensionPackages();
149
150 /** @var ClassLoadingInformationGenerator $generator */
151 $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site, self::isTestingContext());
152
153 $classInformation = $generator->buildClassLoadingInformationForPackage($package);
154 $composerClassLoader->addClassMap($classInformation['classMap']);
155 foreach ($classInformation['psr-4'] as $prefix => $paths) {
156 $composerClassLoader->setPsr4($prefix, $paths);
157 }
158 $classAliasMap = $generator->buildClassAliasMapForPackage($package);
159 if (is_array($classAliasMap) && !empty($classAliasMap['aliasToClassNameMapping']) && !empty($classAliasMap['classNameToAliasMapping'])) {
160 ClassAliasMap::addAliasMap($classAliasMap);
161 }
162 }
163
164 /**
165 * @return string
166 */
167 protected static function getClassLoadingInformationDirectory()
168 {
169 if (self::isTestingContext()) {
170 return PATH_site . self::AUTOLOAD_INFO_DIR_TESTS;
171 }
172 return PATH_site . self::AUTOLOAD_INFO_DIR;
173 }
174
175 /**
176 * Get class name for alias
177 *
178 * @param string $alias
179 * @return mixed
180 */
181 public static function getClassNameForAlias($alias)
182 {
183 return ClassAliasMap::getClassNameForAlias($alias);
184 }
185
186 /**
187 * Ensures the defined path for class information files exists
188 * And clears it in case we're in testing context
189 */
190 protected static function ensureAutoloadInfoDirExists()
191 {
192 $autoloadInfoDir = self::getClassLoadingInformationDirectory();
193 if (!file_exists($autoloadInfoDir)) {
194 GeneralUtility::mkdir_deep($autoloadInfoDir);
195 }
196 }
197
198 /**
199 * Internal method calling the bootstrap to fetch the composer class loader
200 *
201 * @return ClassLoader
202 * @throws \TYPO3\CMS\Core\Exception
203 */
204 protected static function getClassLoader()
205 {
206 return static::$classLoader;
207 }
208
209 /**
210 * Internal method calling the bootstrap to get application context information
211 *
212 * @return bool
213 * @throws \TYPO3\CMS\Core\Exception
214 */
215 protected static function isTestingContext()
216 {
217 return GeneralUtility::getApplicationContext()->isTesting();
218 }
219
220 /**
221 * Get all packages except the protected ones, as they are covered already
222 *
223 * @return PackageInterface[]
224 */
225 protected static function getActiveExtensionPackages()
226 {
227 $activeExtensionPackages = [];
228 $packageManager = GeneralUtility::makeInstance(PackageManager::class);
229 foreach ($packageManager->getActivePackages() as $package) {
230 if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') {
231 // Skip all core packages as the class loading info is prepared for them already
232 continue;
233 }
234 $activeExtensionPackages[] = $package;
235 }
236 return $activeExtensionPackages;
237 }
238 }