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