TYPO3  7.6
ClassAliasMapGenerator.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\ClassAliasLoader;
3 
4 /*
5  * This file is part of the class alias loader package.
6  *
7  * (c) Helmut Hummel <info@helhum.io>
8  *
9  * For the full copyright and license information, please view the LICENSE
10  * file that was distributed with this source code.
11  */
12 
13 use Composer\Composer;
14 use Composer\Config;
15 use Composer\IO\IOInterface;
16 use Composer\IO\NullIO;
17 use Composer\Package\PackageInterface;
18 use Composer\Util\Filesystem;
19 
27 {
31  protected $composer;
32 
36  protected $IO;
37 
41  protected $optimizeAutoloadFiles = false;
42 
48  public function __construct(Composer $composer, IOInterface $IO = null, $optimizeAutoloadFiles = false)
49  {
50  $this->composer = $composer;
51  $this->IO = $IO ?: new NullIO();
52  $this->optimizeAutoloadFiles = $optimizeAutoloadFiles;
53  }
54 
59  public function generateAliasMap()
60  {
61  $config = $this->composer->getConfig();
62 
63  $filesystem = new Filesystem();
64  $filesystem->ensureDirectoryExists($config->get('vendor-dir'));
65  $basePath = $this->extractBasePath($config);
66  $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
67  $targetDir = $vendorPath . '/composer';
68  $filesystem->ensureDirectoryExists($targetDir);
69 
70  $mainPackage = $this->composer->getPackage();
71  $autoLoadGenerator = $this->composer->getAutoloadGenerator();
72  $localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
73  $packageMap = $autoLoadGenerator->buildPackageMap($this->composer->getInstallationManager(), $mainPackage, $localRepo->getCanonicalPackages());
74 
75  $aliasToClassNameMapping = array();
76  $classNameToAliasMapping = array();
77  $classAliasMappingFound = false;
78 
79  foreach ($packageMap as $item) {
81  list($package, $installPath) = $item;
82  $aliasLoaderConfig = new \TYPO3\ClassAliasLoader\Config($package, $this->IO);
83  if ($aliasLoaderConfig->get('class-alias-maps') !== null) {
84  if (!is_array($aliasLoaderConfig->get('class-alias-maps'))) {
85  throw new \Exception('Configuration option "class-alias-maps" must be an array');
86  }
87  foreach ($aliasLoaderConfig->get('class-alias-maps') as $mapFile) {
88  $mapFilePath = ($installPath ?: $basePath) . '/' . $filesystem->normalizePath($mapFile);
89  if (!is_file($mapFilePath)) {
90  $this->IO->writeError(sprintf('The class alias map file "%s" configured in package "%s" was not found!', $mapFile, $package->getName()));
91  } else {
92  $packageAliasMap = require $mapFilePath;
93  if (!is_array($packageAliasMap)) {
94  throw new \Exception('Class alias map files must return an array', 1422625075);
95  }
96  if (!empty($packageAliasMap)) {
97  $classAliasMappingFound = true;
98  }
99  foreach ($packageAliasMap as $aliasClassName => $className) {
100  $lowerCasedAliasClassName = strtolower($aliasClassName);
101  $aliasToClassNameMapping[$lowerCasedAliasClassName] = $className;
102  $classNameToAliasMapping[$className][$lowerCasedAliasClassName] = $lowerCasedAliasClassName;
103  }
104  }
105  }
106  }
107  }
108 
109  $mainPackageAliasLoaderConfig = new \TYPO3\ClassAliasLoader\Config($mainPackage);
110  $alwaysAddAliasLoader = $mainPackageAliasLoaderConfig->get('always-add-alias-loader');
111  $caseSensitiveClassLoading = $mainPackageAliasLoaderConfig->get('autoload-case-sensitivity');
112 
113  if (!$alwaysAddAliasLoader && !$classAliasMappingFound && $caseSensitiveClassLoading) {
114  // No mapping found in any package and no insensitive class loading active. We return early and skip rewriting
115  // Unless user configured alias loader to be always added
116  return false;
117  }
118 
119  $caseSensitiveClassLoadingString = $caseSensitiveClassLoading ? 'true' : 'false';
120  $this->IO->write('<info>Generating ' . ($classAliasMappingFound ? ' ' : 'empty ') . 'class alias map file</info>');
121  $this->generateAliasMapFile($aliasToClassNameMapping, $classNameToAliasMapping, $targetDir);
122 
123  $suffix = null;
124  if (!$config->get('autoloader-suffix') && is_readable($vendorPath . '/autoload.php')) {
125  $content = file_get_contents($vendorPath . '/autoload.php');
126  if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) {
127  $suffix = $match[1];
128  }
129  }
130 
131  if (!$suffix) {
132  $suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
133  }
134 
135  $prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
136 
137  $aliasLoaderInitClassContent = <<<EOF
138 <?php
139 
140 // autoload_alias_loader_real.php @generated by typo3/class-alias-loader
141 
142 class ClassAliasLoaderInit$suffix {
143 
144  private static \$loader;
145 
146  public static function initializeClassAliasLoader(\$composerClassLoader) {
147  if (null !== self::\$loader) {
148  return self::\$loader;
149  }
150  self::\$loader = \$composerClassLoader;
151 
152  \$classAliasMap = require __DIR__ . '/autoload_classaliasmap.php';
153  \$classAliasLoader = new TYPO3\ClassAliasLoader\ClassAliasLoader(\$composerClassLoader);
154  \$classAliasLoader->setAliasMap(\$classAliasMap);
155  \$classAliasLoader->setCaseSensitiveClassLoading($caseSensitiveClassLoadingString);
156  \$classAliasLoader->register($prependAutoloader);
157 
158  TYPO3\ClassAliasLoader\ClassAliasMap::setClassAliasLoader(\$classAliasLoader);
159 
160  return self::\$loader;
161  }
162 }
163 
164 EOF;
165  file_put_contents($targetDir . '/autoload_alias_loader_real.php', $aliasLoaderInitClassContent);
166 
167  if (!$caseSensitiveClassLoading) {
168  $this->IO->write('<info>Re-writing class map to support case insensitive class loading</info>');
169  if (!$this->optimizeAutoloadFiles) {
170  $this->IO->write('<warning>Case insensitive class loading only works reliably if you use the optimize class loading feature of composer</warning>');
171  }
172  $this->rewriteClassMapWithLowerCaseClassNames($targetDir);
173  }
174 
175  $this->IO->write('<info>Inserting class alias loader into main autoload.php file</info>');
176  $this->modifyMainAutoloadFile($vendorPath . '/autoload.php', $suffix);
177 
178  return true;
179  }
180 
185  protected function modifyMainAutoloadFile($autoloadFile, $suffix)
186  {
187  $originalAutoloadFileContent = file_get_contents($autoloadFile);
188  preg_match('/return ComposerAutoloaderInit[^;]*;/', $originalAutoloadFileContent, $matches);
189  $originalAutoloadFileContent = str_replace($matches[0], '', $originalAutoloadFileContent);
190  $composerClassLoaderInit = str_replace(array('return ', ';'), '', $matches[0]);
191  $autoloadFileContent = <<<EOF
192 $originalAutoloadFileContent
193 
194 // autoload.php @generated by typo3/class-alias-loader
195 
196 require_once __DIR__ . '/composer/autoload_alias_loader_real.php';
197 
198 return ClassAliasLoaderInit$suffix::initializeClassAliasLoader($composerClassLoaderInit);
199 
200 EOF;
201 
202  file_put_contents($autoloadFile, $autoloadFileContent);
203 
204  }
205 
211  protected function generateAliasMapFile(array $aliasToClassNameMapping, array $classNameToAliasMapping, $targetDir)
212  {
213  $exportArray = array(
214  'aliasToClassNameMapping' => $aliasToClassNameMapping,
215  'classNameToAliasMapping' => $classNameToAliasMapping
216  );
217 
218  $fileContent = '<?php' . chr(10) . 'return ';
219  $fileContent .= var_export($exportArray, true);
220  $fileContent .= ';';
221 
222  file_put_contents($targetDir . '/autoload_classaliasmap.php', $fileContent);
223  }
224 
231  protected function rewriteClassMapWithLowerCaseClassNames($targetDir)
232  {
233  $classMapContents = file_get_contents($targetDir . '/autoload_classmap.php');
234  $classMapContents = preg_replace_callback('/ \'[^\']*\' => /', function ($match) {
235  return strtolower($match[0]);
236  }, $classMapContents);
237  file_put_contents($targetDir . '/autoload_classmap.php', $classMapContents);
238  }
239 
240 
247  protected function extractBasePath(\Composer\Config $config) {
248  $reflectionClass = new \ReflectionClass($config);
249  $reflectionProperty = $reflectionClass->getProperty('baseDir');
250  $reflectionProperty->setAccessible(true);
251  return $reflectionProperty->getValue($config);
252  }
253 
254 }