94a1f2c263c2224b47b6b201468259a76e010024
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Core / ClassLoadingInformationGenerator.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\ClassMapGenerator;
18 use TYPO3\CMS\Core\Package\Exception\MissingPackageManifestException;
19 use TYPO3\CMS\Core\Package\PackageInterface;
20 use TYPO3\CMS\Core\Package\PackageManager;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Core\Utility\PathUtility;
23
24 /**
25 * Generates class loading information (class maps, class aliases etc.) and writes it to files
26 * for further inclusion in the bootstrap
27 */
28 class ClassLoadingInformationGenerator {
29
30 /**
31 * Folder with framework extensions
32 */
33 const SYSEXT_FOLDER = 'typo3/sysext';
34
35 /**
36 * @var PackageInterface[]
37 */
38 static protected $activeExtensionPackages;
39
40 /**
41 * Returns class loading information for a single package
42 *
43 * @param PackageInterface $package The package to generate the class loading info for
44 * @param bool $useRelativePaths If set to TRUE, make the path relative to the current TYPO3 instance (PATH_site)
45 * @return array
46 */
47 static public function buildClassLoadingInformationForPackage(PackageInterface $package, $useRelativePaths = FALSE) {
48 $classMap = array();
49 $psr4 = array();
50 $packagePath = $package->getPackagePath();
51
52 try {
53 $manifest = self::getPackageManager()->getComposerManifest($package->getPackagePath());
54 if (!empty($manifest->autoload->{'psr-4'})) {
55 $psr4manifest = json_decode(json_encode($manifest->autoload->{'psr-4'}), TRUE);
56 if (is_array($psr4manifest)) {
57 foreach ($psr4manifest as $namespacePrefix => $path) {
58 $namespacePath = $packagePath . $path;
59 if ($useRelativePaths) {
60 $psr4[$namespacePrefix] = self::makePathRelative($namespacePath, realpath($namespacePath));
61 } else {
62 $psr4[$namespacePrefix] = $namespacePath;
63 }
64 }
65 }
66 }
67 } catch (MissingPackageManifestException $e) {
68 // Ignore missing composer manifest
69 }
70
71 foreach (ClassMapGenerator::createMap($packagePath) as $class => $path) {
72 if ($useRelativePaths) {
73 $classMap[$class] = self::makePathRelative($packagePath, $path);
74 } else {
75 $classMap[$class] = $path;
76 }
77 }
78
79 return array('classMap' => $classMap, 'psr-4' => $psr4);
80 }
81
82 /**
83 * Returns class alias map for given package
84 *
85 * @param PackageInterface $package The package to generate the class alias info for
86 * @throws \TYPO3\CMS\Core\Error\Exception
87 * @return array
88 */
89 static public function buildClassAliasMapForPackage(PackageInterface $package) {
90 $aliasToClassNameMapping = array();
91 $classNameToAliasMapping = array();
92 $possibleClassAliasFile = $package->getPackagePath() . 'Migrations/Code/ClassAliasMap.php';
93 if (file_exists($possibleClassAliasFile)) {
94 $packageAliasMap = require $possibleClassAliasFile;
95 if (!is_array($packageAliasMap)) {
96 throw new \TYPO3\CMS\Core\Error\Exception('"class alias maps" must return an array', 1422625075);
97 }
98 foreach ($packageAliasMap as $aliasClassName => $className) {
99 $lowerCasedAliasClassName = strtolower($aliasClassName);
100 $aliasToClassNameMapping[$lowerCasedAliasClassName] = $className;
101 $classNameToAliasMapping[$className][$lowerCasedAliasClassName] = $lowerCasedAliasClassName;
102 }
103 }
104
105 return array('aliasToClassNameMapping' => $aliasToClassNameMapping, 'classNameToAliasMapping' => $classNameToAliasMapping);
106 }
107
108 /**
109 * Generate the class map file
110 * @return string[]
111 * @internal
112 */
113 static public function buildAutoloadInformationFiles() {
114 // Ensure that for each re-build, the packages are fetched again from the package manager
115 self::$activeExtensionPackages = NULL;
116
117 $psr4File = $classMapFile = <<<EOF
118 <?php
119
120 // autoload_classmap.php @generated by TYPO3
121
122 \$typo3InstallDir = PATH_site;
123
124 return array(
125
126 EOF;
127 $classMap = array();
128 $psr4 = array();
129 foreach (self::getActiveExtensionPackages() as $package) {
130 $classLoadingInformation = self::buildClassLoadingInformationForPackage($package, TRUE);
131 $classMap = array_merge($classMap, $classLoadingInformation['classMap']);
132 $psr4 = array_merge($psr4, $classLoadingInformation['psr-4']);
133 }
134
135 ksort($classMap);
136 ksort($psr4);
137 foreach ($classMap as $class => $relativePath) {
138 $classMapFile .= sprintf(' %s => %s,', var_export($class, TRUE), self::getPathCode($relativePath)) . LF;
139 }
140 $classMapFile .= ");\n";
141
142 foreach ($psr4 as $prefix => $relativePath) {
143 $psr4File .= sprintf(' %s => array(%s),', var_export($prefix, TRUE), self::getPathCode($relativePath)) . LF;
144 }
145 $psr4File .= ");\n";
146
147 return array('classMapFile' => $classMapFile, 'psr-4File' => $psr4File);
148 }
149
150 /**
151 * Generate a relative path string from an absolute path within a give package path
152 *
153 * @param string $packagePath
154 * @param string $realPathOfClassFile
155 * @return string
156 */
157 static protected function makePathRelative($packagePath, $realPathOfClassFile) {
158 $realPathOfClassFile = GeneralUtility::fixWindowsFilePath($realPathOfClassFile);
159 $classesRealPath = GeneralUtility::fixWindowsFilePath(realpath($packagePath));
160 $relativeClassesPath = rtrim(PathUtility::stripPathSitePrefix($packagePath), '/');
161 $relativePathToClassFile = $relativeClassesPath . '/' . ltrim(substr($realPathOfClassFile, strlen($classesRealPath)), '/');
162
163 return $relativePathToClassFile;
164 }
165
166 /**
167 * Generate a relative path string from a relative path
168 *
169 * @param string $relativePathToClassFile
170 * @return string
171 */
172 static protected function getPathCode($relativePathToClassFile) {
173 return '$typo3InstallDir . ' . var_export($relativePathToClassFile, TRUE);
174 }
175
176 /**
177 * Build class alias mapping file
178 *
179 * @return string
180 * @throws \Exception
181 * @internal
182 */
183 static public function buildClassAliasMapFile() {
184 $aliasToClassNameMapping = array();
185 $classNameToAliasMapping = array();
186 foreach (self::getActiveExtensionPackages() as $package) {
187 $aliasMappingForPackage = self::buildClassAliasMapForPackage($package);
188 $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $aliasMappingForPackage['aliasToClassNameMapping']);
189 $classNameToAliasMapping = array_merge($classNameToAliasMapping, $aliasMappingForPackage['classNameToAliasMapping']);
190 }
191 $exportArray = array(
192 'aliasToClassNameMapping' => $aliasToClassNameMapping,
193 'classNameToAliasMapping' => $classNameToAliasMapping
194 );
195 $fileContent = '<?php' . chr(10) . 'return ';
196 $fileContent .= var_export($exportArray, TRUE);
197 $fileContent .= ";\n";
198 return $fileContent;
199 }
200
201 /**
202 * Get all packages except the protected ones, as they are covered already
203 *
204 * @return \TYPO3\CMS\Core\Package\PackageInterface[]
205 */
206 static protected function getActiveExtensionPackages() {
207 if (self::$activeExtensionPackages === NULL) {
208 self::$activeExtensionPackages = array();
209 foreach (self::getPackageManager()->getActivePackages() as $package) {
210 if (self::isFrameworkPackage($package)) {
211 // Skip all core packages as the class loading info is prepared for them already
212 continue;
213 }
214 self::$activeExtensionPackages[] = $package;
215 }
216 }
217
218 return self::$activeExtensionPackages;
219 }
220
221 /**
222 * Check if the package is a framework package (located in typo3/sysext)
223 *
224 * @param PackageInterface $package
225 * @return bool
226 */
227 static protected function isFrameworkPackage(PackageInterface $package) {
228 return strpos($package->getPackagePath(), self::SYSEXT_FOLDER) !== FALSE;
229 }
230
231 /**
232 * @return PackageManager
233 * @throws \TYPO3\CMS\Core\Exception
234 */
235 static protected function getPackageManager() {
236 return Bootstrap::getInstance()->getEarlyInstance(PackageManager::class);
237 }
238
239 }