‪TYPO3CMS  ‪main
ClassLoadingInformationGenerator.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 namespace ‪TYPO3\CMS\Core\Core;
19 
20 use Composer\Autoload\ClassLoader;
21 use Composer\Autoload\ClassMapGenerator;
25 
31 class ClassLoadingInformationGenerator
32 {
36  protected $activeExtensionPackages;
37 
41  protected $classLoader;
42 
46  protected $installationRoot;
47 
51  protected $isDevMode;
52 
57  public function __construct(ClassLoader $classLoader, array $activeExtensionPackages, $installationRoot, $isDevMode = false)
58  {
59  $this->classLoader = $classLoader;
60  $this->activeExtensionPackages = $activeExtensionPackages;
61  $this->installationRoot = $installationRoot;
62  $this->isDevMode = $isDevMode;
63  }
64 
72  public function buildClassLoadingInformationForPackage(PackageInterface $package, $useRelativePaths = false)
73  {
74  $classMap = [];
75  $psr4 = [];
76  $packagePath = $package->getPackagePath();
77  $manifest = $package->getValueFromComposerManifest();
78 
79  if (empty($manifest->autoload)) {
80  // Legacy mode: Scan the complete extension directory for class files
81  $classMap = $this->createClassMap($packagePath, $useRelativePaths, !$this->isDevMode);
82  } else {
83  $autoloadPsr4 = $this->getAutoloadSectionFromManifest($manifest, 'psr-4');
84  if (!empty($autoloadPsr4)) {
85  foreach ($autoloadPsr4 as $namespacePrefix => $paths) {
86  foreach ((array)$paths as $path) {
87  $namespacePath = $packagePath . $path;
88  $namespaceRealPath = (string)realpath($namespacePath);
89  if ($useRelativePaths) {
90  $psr4[$namespacePrefix][] = $this->makePathRelative($namespacePath, $namespaceRealPath);
91  } else {
92  $psr4[$namespacePrefix][] = $namespacePath;
93  }
94  if (!empty($namespaceRealPath) && is_dir($namespaceRealPath)) {
95  // Add all prs-4 classes to the class map for improved class loading performance
96  $classMap = array_merge($classMap, $this->createClassMap($namespacePath, $useRelativePaths, false, $namespacePrefix));
97  }
98  }
99  }
100  }
101  $autoloadClassmap = $this->getAutoloadSectionFromManifest($manifest, 'classmap');
102  if (!empty($autoloadClassmap)) {
103  foreach ($autoloadClassmap as $path) {
104  $classMap = array_merge($classMap, $this->createClassMap($packagePath . $path, $useRelativePaths));
105  }
106  }
107  }
108 
109  return ['classMap' => $classMap, 'psr-4' => $psr4];
110  }
111 
120  protected function getAutoloadSectionFromManifest($manifest, $section)
121  {
122  $finalAutoloadSection = [];
123  $autoloadDefinition = json_decode((string)json_encode($manifest->autoload), true);
124  if (!empty($autoloadDefinition[$section]) && is_array($autoloadDefinition[$section])) {
125  $finalAutoloadSection = $autoloadDefinition[$section];
126  }
127  if ($this->isDevMode) {
128  if (isset($manifest->{'autoload-dev'})) {
129  $autoloadDefinitionDev = json_decode((string)json_encode($manifest->{'autoload-dev'}), true);
130  if (!empty($autoloadDefinitionDev[$section]) && is_array($autoloadDefinitionDev[$section])) {
131  $finalAutoloadSection = array_merge($finalAutoloadSection, $autoloadDefinitionDev[$section]);
132  }
133  }
134  }
135 
136  return $finalAutoloadSection;
137  }
138 
148  protected function createClassMap($classesPath, $useRelativePaths = false, $ignorePotentialTestClasses = false, $namespace = null)
149  {
150  $classMap = [];
151  $blacklistExpression = null;
152  if ($ignorePotentialTestClasses) {
153  $blacklistPathPrefix = (string)realpath($classesPath);
154  $blacklistPathPrefix = str_replace('\\', '/', $blacklistPathPrefix);
155  $blacklistExpression = "{($blacklistPathPrefix/tests/|$blacklistPathPrefix/Tests/|$blacklistPathPrefix/Resources/|$blacklistPathPrefix/res/|$blacklistPathPrefix/class.ext_update.php)}";
156  }
157  foreach (ClassMapGenerator::createMap($classesPath, $blacklistExpression, null, $namespace) as $class => $path) {
158  if ($useRelativePaths) {
159  $classMap[$class] = $this->makePathRelative($classesPath, $path);
160  } else {
161  $classMap[$class] = $path;
162  }
163  }
164  return $classMap;
165  }
166 
174  public function buildClassAliasMapForPackage(PackageInterface $package)
175  {
176  $aliasToClassNameMapping = [];
177  $classNameToAliasMapping = [];
178  $possibleClassAliasFiles = [];
179  $manifest = $package->getValueFromComposerManifest();
180  if (!empty($manifest->extra->{'typo3/class-alias-loader'}->{'class-alias-maps'})) {
181  $possibleClassAliasFiles = $manifest->extra->{'typo3/class-alias-loader'}->{'class-alias-maps'};
182  if (!is_array($possibleClassAliasFiles)) {
183  throw new Exception('"typo3/class-alias-loader"/"class-alias-maps" must return an array!', 1444142481);
184  }
185  } else {
186  $possibleClassAliasFiles[] = 'Migrations/Code/ClassAliasMap.php';
187  }
188  $packagePath = $package->getPackagePath();
189  foreach ($possibleClassAliasFiles as $possibleClassAliasFile) {
190  $possiblePathToClassAliasFile = $packagePath . $possibleClassAliasFile;
191  if (file_exists($possiblePathToClassAliasFile)) {
192  $packageAliasMap = require $possiblePathToClassAliasFile;
193  if (!is_array($packageAliasMap)) {
194  throw new Exception('"class alias maps" must return an array', 1422625075);
195  }
196  foreach ($packageAliasMap as $aliasClassName => $className) {
197  $lowerCasedAliasClassName = strtolower($aliasClassName);
198  $aliasToClassNameMapping[$lowerCasedAliasClassName] = $className;
199  $classNameToAliasMapping[$className][$lowerCasedAliasClassName] = $lowerCasedAliasClassName;
200  }
201  }
202  }
203 
204  return ['aliasToClassNameMapping' => $aliasToClassNameMapping, 'classNameToAliasMapping' => $classNameToAliasMapping];
205  }
206 
212  public function buildAutoloadInformationFiles()
213  {
214  $psr4File = $classMapFile = <<<EOF
215 <?php
216 
217 // autoload_classmap.php @generated by TYPO3
218 
219 \$typo3InstallDir = ‪\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/';
220 
221 return array(
222 
223 EOF;
224  $classMap = [];
225  $psr4 = [];
226  foreach ($this->activeExtensionPackages as $package) {
227  $classLoadingInformation = $this->buildClassLoadingInformationForPackage($package, true);
228  $classMap = array_merge($classMap, $classLoadingInformation['classMap']);
229  $psr4 = array_merge($psr4, $classLoadingInformation['psr-4']);
230  }
231 
232  ksort($classMap);
233  ksort($psr4);
234  foreach ($classMap as $class => $relativePath) {
235  $classMapFile .= sprintf(' %s => %s,', var_export($class, true), $this->getPathCode($relativePath)) . "\n";
236  }
237  $classMapFile .= ");\n";
238 
239  foreach ($psr4 as $prefix => $relativePaths) {
240  $psr4File .= sprintf(' %s => array(%s),', var_export($prefix, true), implode(',', array_map($this->getPathCode(...), $relativePaths))) . "\n";
241  }
242  $psr4File .= ");\n";
243 
244  return ['classMapFile' => $classMapFile, 'psr-4File' => $psr4File];
245  }
246 
255  protected function makePathRelative($packagePath, $realPathOfClassFile, $relativeToRoot = true)
256  {
257  $realPathOfClassFile = GeneralUtility::fixWindowsFilePath((string)$realPathOfClassFile);
258  $packageRealPath = GeneralUtility::fixWindowsFilePath((string)realpath($packagePath));
259  $relativePackagePath = rtrim(substr($packagePath, strlen($this->installationRoot)), '/');
260  if ($relativeToRoot) {
261  if ($realPathOfClassFile === $packageRealPath) {
262  $relativePathToClassFile = $relativePackagePath;
263  } else {
264  $relativePathToClassFile = $relativePackagePath . '/' . ltrim(substr($realPathOfClassFile, strlen($packageRealPath)), '/');
265  }
266  } else {
267  $relativePathToClassFile = ltrim(substr($realPathOfClassFile, strlen($packageRealPath)), '/');
268  }
269 
270  return $relativePathToClassFile;
271  }
272 
279  protected function getPathCode($relativePathToClassFile)
280  {
281  return '$typo3InstallDir . ' . var_export($relativePathToClassFile, true);
282  }
283 
291  public function buildClassAliasMapFile()
292  {
293  $aliasToClassNameMapping = [];
294  $classNameToAliasMapping = [];
295  foreach ($this->activeExtensionPackages as $package) {
296  $aliasMappingForPackage = $this->buildClassAliasMapForPackage($package);
297  $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $aliasMappingForPackage['aliasToClassNameMapping']);
298  $classNameToAliasMapping = array_merge($classNameToAliasMapping, $aliasMappingForPackage['classNameToAliasMapping']);
299  }
300  $exportArray = [
301  'aliasToClassNameMapping' => $aliasToClassNameMapping,
302  'classNameToAliasMapping' => $classNameToAliasMapping,
303  ];
304  $fileContent = "<?php\nreturn ";
305  $fileContent .= var_export($exportArray, true);
306  $fileContent .= ";\n";
307  return $fileContent;
308  }
309 }
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Core\Package\PackageInterface
Definition: PackageInterface.php:24
‪TYPO3\CMS\Core\Error\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Core
Definition: ApplicationContext.php:16
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52