Переглянути джерело

Merge branch 'master' into 2.0

Jordi Boggiano 5 роки тому
батько
коміт
0ea06de286

+ 13 - 0
CHANGELOG.md

@@ -1,3 +1,15 @@
+### [1.9.1] 2019-11-01
+
+  * Fixed various credential handling issues with gitlab and github
+  * Fixed credentials being present in git remotes in Composer cache and vendor directory when not using SSH keys
+  * Fixed `composer why` not listing replacers as a reason something is present
+  * Fixed various PHP 7.4 compatibility issues
+  * Fixed root warnings always present in Docker containers, setting COMPOSER_ALLOW_SUPERUSER is not necessary anymore
+  * Fixed GitHub access tokens leaking into debug-verbosity output
+  * Fixed several edge case issues detecting GitHub, Bitbucket and GitLab repository types
+  * Fixed Composer asking if you want to use a composer.json in a parent directory when ran in non-interactive mode
+  * Fixed classmap autoloading issue finding classes located within a few non-PHP context blocks (?>...<?php)
+
 ### [1.9.0] 2019-08-02
 
   * Breaking: artifact repositories with URLs containing port numbers and requiring authentication now require you to configure http-basic auth for the `host:port` pair explicitly
@@ -764,6 +776,7 @@
 
   * Initial release
 
+[1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1
 [1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0
 [1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
 [1.8.5]: https://github.com/composer/composer/compare/1.8.4...1.8.5

+ 7 - 8
src/Composer/Autoload/AutoloadGenerator.php

@@ -256,15 +256,14 @@ EOF;
                             continue;
                         }
 
-                        $namespaceFilter = $namespace === '' ? null : $namespace;
-                        $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $classMap);
+                        $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap);
                     }
                 }
             }
         }
 
         foreach ($autoloads['classmap'] as $dir) {
-            $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, $classMap);
+            $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap);
         }
 
         ksort($classMap);
@@ -317,9 +316,9 @@ EOF;
         return count($classMap);
     }
 
-    private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, array $classMap = array())
+    private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array())
     {
-        foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter) as $class => $path) {
+        foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) {
             $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
             if (!isset($classMap[$class])) {
                 $classMap[$class] = $pathCode;
@@ -334,9 +333,9 @@ EOF;
         return $classMap;
     }
 
-    private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null, $showAmbiguousWarning = true)
+    private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, $showAmbiguousWarning = true)
     {
-        return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter);
+        return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType);
     }
 
     public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
@@ -447,7 +446,7 @@ EOF;
 
             foreach ($autoloads['classmap'] as $dir) {
                 try {
-                    $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, false));
+                    $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false));
                 } catch (\RuntimeException $e) {
                     $this->io->writeError('<warning>'.$e->getMessage().'</warning>');
                 }

+ 80 - 6
src/Composer/Autoload/ClassMapGenerator.php

@@ -50,17 +50,19 @@ class ClassMapGenerator
     /**
      * Iterate over all files in the given directory searching for classes
      *
-     * @param \Iterator|string $path      The path to search in or an iterator
-     * @param string           $blacklist Regex that matches against the file path that exclude from the classmap.
-     * @param IOInterface      $io        IO object
-     * @param string           $namespace Optional namespace prefix to filter by
+     * @param \Iterator|string $path         The path to search in or an iterator
+     * @param string           $blacklist    Regex that matches against the file path that exclude from the classmap.
+     * @param IOInterface      $io           IO object
+     * @param string           $namespace    Optional namespace prefix to filter by
+     * @param string           $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules
      *
      * @throws \RuntimeException When the path is neither an existing file nor directory
      * @return array             A class map array
      */
-    public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null)
+    public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null, $autoloadType = null)
     {
         if (is_string($path)) {
+            $basePath = $path;
             if (is_file($path)) {
                 $path = array(new \SplFileInfo($path));
             } elseif (is_dir($path)) {
@@ -71,6 +73,8 @@ class ClassMapGenerator
                     '" which does not appear to be a file nor a folder'
                 );
             }
+        } elseif (null !== $autoloadType) {
+            throw new \RuntimeException('Path must be a string when specifying an autoload type');
         }
 
         $map = array();
@@ -100,10 +104,14 @@ class ClassMapGenerator
             }
 
             $classes = self::findClasses($filePath);
+            if (null !== $autoloadType) {
+                $classes = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io);
+            }
 
             foreach ($classes as $class) {
                 // skip classes not within the given namespace prefix
-                if (null !== $namespace && 0 !== strpos($class, $namespace)) {
+                // TODO enable in Composer v1.11 or 2.0 whichever comes first
+                if (/* null === $autoloadType && */ null !== $namespace && 0 !== strpos($class, $namespace)) {
                     continue;
                 }
 
@@ -121,6 +129,72 @@ class ClassMapGenerator
         return $map;
     }
 
+    /**
+     * Remove classes which could not have been loaded by namespace autoloaders
+     *
+     * @param array       $classes       found classes in given file
+     * @param string      $filePath      current file
+     * @param string      $baseNamespace prefix of given autoload mapping
+     * @param string      $namespaceType psr-0|psr-4
+     * @param string      $basePath      root directory of given autoload mapping
+     * @param IOInterface $io            IO object
+     * @return array      valid classes
+     */
+    private static function filterByNamespace($classes, $filePath, $baseNamespace, $namespaceType, $basePath, $io)
+    {
+        $validClasses = array();
+        $rejectedClasses = array();
+
+        $realSubPath = substr($filePath, strlen($basePath) + 1);
+        $realSubPath = substr($realSubPath, 0, strrpos($realSubPath, '.'));
+
+        foreach ($classes as $class) {
+            // silently skip if ns doesn't have common root
+            if ('' !== $baseNamespace && 0 !== strpos($class, $baseNamespace)) {
+                continue;
+            }
+            // transform class name to file path and validate
+            if ('psr-0' === $namespaceType) {
+                $namespaceLength = strrpos($class, '\\');
+                if (false !== $namespaceLength) {
+                    $namespace = substr($class, 0, $namespaceLength + 1);
+                    $className = substr($class, $namespaceLength + 1);
+                    $subPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespace)
+                        . str_replace('_', DIRECTORY_SEPARATOR, $className);
+                }
+                else {
+                    $subPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
+                }
+            } elseif ('psr-4' === $namespaceType) {
+                $subNamespace = ('' !== $baseNamespace) ? substr($class, strlen($baseNamespace)) : $class;
+                $subPath = str_replace('\\', DIRECTORY_SEPARATOR, $subNamespace);
+            } else {
+                throw new \RuntimeException("namespaceType must be psr-0 or psr-4, $namespaceType given");
+            }
+            if ($subPath === $realSubPath) {
+                $validClasses[] = $class;
+            } else {
+                $rejectedClasses[] = $class;
+            }
+        }
+        // warn only if no valid classes, else silently skip invalid
+        if (empty($validClasses)) {
+            foreach ($rejectedClasses as $class) {
+                trigger_error(
+                    "Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. It will not autoload anymore in Composer v1.11+.",
+                    E_USER_DEPRECATED
+                );
+            }
+
+            // TODO enable in Composer v1.11 or 2.0 whichever comes first
+            //return array();
+        }
+
+        // TODO enable in Composer v1.11 or 2.0 whichever comes first & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses
+        //return $validClasses;
+        return $classes;
+    }
+
     /**
      * Extract the classes in the given file
      *

+ 6 - 6
src/Composer/Command/DumpAutoloadCommand.php

@@ -63,11 +63,11 @@ EOT
         $apcu = $input->getOption('apcu') || $config->get('apcu-autoloader');
 
         if ($authoritative) {
-            $this->getIO()->writeError('<info>Generating optimized autoload files (authoritative)</info>', false);
+            $this->getIO()->write('<info>Generating optimized autoload files (authoritative)</info>');
         } elseif ($optimize) {
-            $this->getIO()->writeError('<info>Generating optimized autoload files</info>', false);
+            $this->getIO()->write('<info>Generating optimized autoload files</info>');
         } else {
-            $this->getIO()->writeError('<info>Generating autoload files</info>', false);
+            $this->getIO()->write('<info>Generating autoload files</info>');
         }
 
         $generator = $composer->getAutoloadGenerator();
@@ -78,11 +78,11 @@ EOT
         $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
 
         if ($authoritative) {
-            $this->getIO()->overwriteError('<info>Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes</info>');
+            $this->getIO()->write('<info>Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes</info>');
         } elseif ($optimize) {
-            $this->getIO()->overwriteError('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>');
+            $this->getIO()->write('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>');
         } else {
-            $this->getIO()->overwriteError('<info>Generated autoload files containing '. $numberOfClasses .' classes</info>');
+            $this->getIO()->write('<info>Generated autoload files containing '. $numberOfClasses .' classes</info>');
         }
 
         return 0;

+ 9 - 1
src/Composer/Command/RequireCommand.php

@@ -159,7 +159,15 @@ EOT
         }
 
         $phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion();
-        $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed'));
+        try {
+            $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed'));
+        } catch (\Exception $e) {
+            if ($this->newlyCreated) {
+                throw new \RuntimeException('No composer.json present in the current directory, this may be the cause of the following exception.', 0, $e);
+            }
+
+            throw $e;
+        }
 
         $requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
         $removeKey = $input->getOption('dev') ? 'require' : 'require-dev';

+ 42 - 0
tests/Composer/Test/Autoload/AutoloadGeneratorTest.php

@@ -548,6 +548,48 @@ class AutoloadGeneratorTest extends TestCase
         );
     }
 
+    public function testPSRToClassMapIgnoresNonPSRClasses()
+    {
+        $package = new Package('a', '1.0', '1.0');
+
+        $this->markTestSkipped('Skipped until ClassMapGenerator ignoring of invalid PSR-x classes is enabled');
+
+        $package->setAutoload(array(
+            'psr-0' => array('psr0_' => 'psr0/'),
+            'psr-4' => array('psr4\\' => 'psr4/'),
+        ));
+
+        $this->repository->expects($this->once())
+            ->method('getCanonicalPackages')
+            ->will($this->returnValue(array()));
+
+        $this->fs->ensureDirectoryExists($this->workingDir.'/psr0/psr0');
+        $this->fs->ensureDirectoryExists($this->workingDir.'/psr4');
+        file_put_contents($this->workingDir.'/psr0/psr0/match.php', '<?php class psr0_match {}');
+        file_put_contents($this->workingDir.'/psr0/psr0/badfile.php', '<?php class psr0_badclass {}');
+        file_put_contents($this->workingDir.'/psr4/match.php', '<?php namespace psr4; class match {}');
+        file_put_contents($this->workingDir.'/psr4/badfile.php', '<?php namespace psr4; class badclass {}');
+
+        $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1');
+        $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated.");
+
+        $expectedClassmap = <<<EOF
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+\$vendorDir = dirname(dirname(__FILE__));
+\$baseDir = dirname(\$vendorDir);
+
+return array(
+    'psr0_match' => \$baseDir . '/psr0/psr0/match.php',
+    'psr4\\\\match' => \$baseDir . '/psr4/match.php',
+);
+
+EOF;
+        $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_classmap.php', $expectedClassmap);
+    }
+
     public function testVendorsClassMapAutoloading()
     {
         $package = new Package('a', '1.0', '1.0');