diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15526d860..ade8d99cd 100644
--- a/CHANGELOG.md
+++ b/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 (?>...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(''.$e->getMessage().'');
}
diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php
index 8467c742e..0a2123d20 100644
--- a/src/Composer/Autoload/ClassMapGenerator.php
+++ b/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
*
diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php
index dbda29d63..f1e91f15c 100644
--- a/src/Composer/Command/DumpAutoloadCommand.php
+++ b/src/Composer/Command/DumpAutoloadCommand.php
@@ -63,11 +63,11 @@ EOT
$apcu = $input->getOption('apcu') || $config->get('apcu-autoloader');
if ($authoritative) {
- $this->getIO()->writeError('Generating optimized autoload files (authoritative)', false);
+ $this->getIO()->write('Generating optimized autoload files (authoritative)');
} elseif ($optimize) {
- $this->getIO()->writeError('Generating optimized autoload files', false);
+ $this->getIO()->write('Generating optimized autoload files');
} else {
- $this->getIO()->writeError('Generating autoload files', false);
+ $this->getIO()->write('Generating autoload files');
}
$generator = $composer->getAutoloadGenerator();
@@ -78,11 +78,11 @@ EOT
$numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
if ($authoritative) {
- $this->getIO()->overwriteError('Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes');
+ $this->getIO()->write('Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes');
} elseif ($optimize) {
- $this->getIO()->overwriteError('Generated optimized autoload files containing '. $numberOfClasses .' classes');
+ $this->getIO()->write('Generated optimized autoload files containing '. $numberOfClasses .' classes');
} else {
- $this->getIO()->overwriteError('Generated autoload files containing '. $numberOfClasses .' classes');
+ $this->getIO()->write('Generated autoload files containing '. $numberOfClasses .' classes');
}
return 0;
diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php
index 56ec8bf54..af16a3898 100644
--- a/src/Composer/Command/RequireCommand.php
+++ b/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';
diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
index 84ac16df7..ba89ccea9 100644
--- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
+++ b/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', 'workingDir.'/psr0/psr0/badfile.php', 'workingDir.'/psr4/match.php', 'workingDir.'/psr4/badfile.php', '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 = << \$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');