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