3 * Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
5 * This file is released under the terms of the MIT license. You can find the
6 * complete text in the attached LICENSE file or online at:
8 * http://www.opensource.org/licenses/mit-license.php
10 * --------------------------------------------------------------------------
12 * 99% of this is copied as-is from the original Composer source code and is
13 * released under MIT license as well. Copyright goes to:
15 * - Igor Wiedler <igor@wiedler.ch>
16 * - Jordi Boggiano <j.boggiano@seld.be>
19 namespace xrstf\Composer52;
21 use Composer\Autoload\AutoloadGenerator as BaseGenerator;
22 use Composer\Autoload\ClassMapGenerator;
24 use Composer\Installer\InstallationManager;
25 use Composer\Package\AliasPackage;
26 use Composer\Package\PackageInterface;
27 use Composer\Repository\InstalledRepositoryInterface;
28 use Composer\Util\Filesystem;
30 class AutoloadGenerator extends BaseGenerator {
31 public function __construct() {
32 // do nothing (but keep this constructor so we can build an instance without the need for an event dispatcher)
35 public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') {
36 $filesystem = new Filesystem();
37 $filesystem->ensureDirectoryExists($config->get('vendor-dir'));
40 $basePath = $filesystem->normalizePath($cwd);
41 $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
42 $targetDir = $vendorPath.'/'.$targetDir;
43 $filesystem->ensureDirectoryExists($targetDir);
45 $useGlobalIncludePath = (bool) $config->get('use-include-path');
46 $prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
47 $classMapAuthoritative = $config->get('classmap-authoritative');
49 $vendorPathCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true);
50 $vendorPathToTargetDirCode = $filesystem->findShortestPathCode($vendorPath, realpath($targetDir), true);
52 $appBaseDirCode = $filesystem->findShortestPathCode($vendorPath, $basePath, true);
53 $appBaseDirCode = str_replace('__DIR__', '$vendorDir', $appBaseDirCode);
56 $vendorPathCode = str_replace('__DIR__', 'dirname(__FILE__)', $vendorPathCode);
57 $vendorPathToTargetDirCode = str_replace('__DIR__', 'dirname(__FILE__)', $vendorPathToTargetDirCode);
59 $packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages());
60 $autoloads = $this->parseAutoloads($packageMap, $mainPackage);
62 // add custom psr-0 autoloading if the root package has a target dir
63 $targetDirLoader = null;
64 $mainAutoload = $mainPackage->getAutoload();
65 if ($mainPackage->getTargetDir() && !empty($mainAutoload['psr-0'])) {
66 $levels = count(explode('/', $filesystem->normalizePath($mainPackage->getTargetDir())));
67 $prefixes = implode(', ', array_map(function ($prefix) {
68 return var_export($prefix, true);
69 }, array_keys($mainAutoload['psr-0'])));
71 $baseDirFromTargetDirCode = $filesystem->findShortestPathCode($targetDir, $basePath, true);
73 $targetDirLoader = <<<EOF
75 public static function autoload(\$class) {
76 \$dir = $baseDirFromTargetDirCode.'/';
77 \$prefixes = array($prefixes);
79 foreach (\$prefixes as \$prefix) {
80 if (0 !== strpos(\$class, \$prefix)) {
84 \$path = explode(DIRECTORY_SEPARATOR, self::getClassPath(\$class));
85 \$path = \$dir.implode('/', array_slice(\$path, $levels));
87 if (!\$path = self::resolveIncludePath(\$path)) {
100 $autoloads['files'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['files']));
101 foreach ($autoloads['files'] as $functionFile) {
102 // don't include file if it is using PHP 5.3+ syntax
103 // https://bitbucket.org/xrstf/composer-php52/issue/4
104 if ($this->isPHP53($functionFile)) {
105 $filesCode .= '// require '.$this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile)."; // disabled because of PHP 5.3 syntax\n";
108 $filesCode .= ' require '.$this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile).";\n";
113 $suffix = md5(uniqid('', true));
116 $includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode);
118 file_put_contents($vendorPath.'/autoload_52.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
119 file_put_contents($targetDir.'/autoload_real_52.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, $filesCode, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative));
121 // use stream_copy_to_stream instead of copy
122 // to work around https://bugs.php.net/bug.php?id=64634
123 $sourceLoader = fopen(__DIR__.'/ClassLoader.php', 'r');
124 $targetLoader = fopen($targetDir.'/ClassLoader52.php', 'w+');
125 stream_copy_to_stream($sourceLoader, $targetLoader);
126 fclose($sourceLoader);
127 fclose($targetLoader);
128 unset($sourceLoader, $targetLoader);
131 protected function isPHP53($file) {
132 $tokens = token_get_all(file_get_contents($file));
133 $php53 = array(T_DIR, T_GOTO, T_NAMESPACE, T_NS_C, T_NS_SEPARATOR, T_USE);
136 if (defined('T_TRAIT')) {
138 $php53[] = T_TRAIT_C;
139 $php53[] = T_TRAIT_C;
143 if (defined('T_FINALLY')) {
144 $php53[] = T_FINALLY;
148 foreach ($tokens as $token) {
149 if (is_array($token) && in_array($token[0], $php53)) {
157 protected function getIncludePathsFile(array $packageMap, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode) {
158 $includePaths = array();
160 foreach ($packageMap as $item) {
161 list($package, $installPath) = $item;
163 if (null !== $package->getTargetDir() && strlen($package->getTargetDir()) > 0) {
164 $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir()));
167 foreach ($package->getIncludePaths() as $includePath) {
168 $includePath = trim($includePath, '/');
169 $includePaths[] = empty($installPath) ? $includePath : $installPath.'/'.$includePath;
173 if (!$includePaths) {
177 $includePathsFile = <<<EOF
180 // include_paths_52.php generated by xrstf/composer-php52
182 \$vendorDir = $vendorPathCode;
183 \$baseDir = $appBaseDirCode;
189 foreach ($includePaths as $path) {
190 $includePathsFile .= "\t" . $this->getPathCode($filesystem, $basePath, $vendorPath, $path) . ",\n";
193 return $includePathsFile . ");\n";
196 protected function getAutoloadFile($vendorPathToTargetDirCode, $suffix) {
200 // autoload_52.php generated by xrstf/composer-php52
202 require_once $vendorPathToTargetDirCode.'/autoload_real_52.php';
204 return ComposerAutoloaderInit$suffix::getLoader();
209 protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $filesCode, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative) {
210 // TODO the class ComposerAutoloaderInit should be revert to a closure
211 // when APC has been fixed:
212 // - https://github.com/composer/composer/issues/959
213 // - https://bugs.php.net/bug.php?id=52144
214 // - https://bugs.php.net/bug.php?id=61576
215 // - https://bugs.php.net/bug.php?id=59298
218 $filesCode = "\n\n".rtrim($filesCode);
224 // autoload_real_52.php generated by xrstf/composer-php52
226 class ComposerAutoloaderInit$suffix {
227 private static \$loader;
229 public static function loadClassLoader(\$class) {
230 if ('xrstf_Composer52_ClassLoader' === \$class) {
231 require dirname(__FILE__).'/ClassLoader52.php';
236 * @return xrstf_Composer52_ClassLoader
238 public static function getLoader() {
239 if (null !== self::\$loader) {
240 return self::\$loader;
243 spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'), true /*, true */);
244 self::\$loader = \$loader = new xrstf_Composer52_ClassLoader();
245 spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));
247 \$vendorDir = $vendorPathCode;
248 \$baseDir = $appBaseDirCode;
249 \$dir = dirname(__FILE__);
254 if ($useIncludePath) {
255 $file .= <<<'INCLUDE_PATH'
256 $includePaths = require $dir.'/include_paths.php';
257 array_push($includePaths, get_include_path());
258 set_include_path(implode(PATH_SEPARATOR, $includePaths));
265 $map = require $dir.'/autoload_namespaces.php';
266 foreach ($map as $namespace => $path) {
267 $loader->add($namespace, $path);
274 $file .= <<<'CLASSMAP'
275 $classMap = require $dir.'/autoload_classmap.php';
277 $loader->addClassMap($classMap);
284 if ($classMapAuthoritative) {
285 $file .= <<<'CLASSMAPAUTHORITATIVE'
286 $loader->setClassMapAuthoritative(true);
288 CLASSMAPAUTHORITATIVE;
291 if ($useGlobalIncludePath) {
292 $file .= <<<'INCLUDEPATH'
293 $loader->setUseIncludePath(true);
299 if ($targetDirLoader) {
300 $file .= <<<REGISTER_AUTOLOAD
301 spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'autoload'), true);
308 $file .= <<<METHOD_FOOTER
309 \$loader->register($prependAutoloader);{$filesCode}
316 $file .= $targetDirLoader;
318 return $file . <<<FOOTER