Merge pull request #8949 from Seldaek/cleanups

Cleanups / docblocks
main
Nils Adermann 4 years ago committed by GitHub
commit f15b9c258e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -53,4 +53,4 @@ jobs:
- name: Run PHPStan - name: Run PHPStan
run: | run: |
bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies
vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr vendor/bin/phpstan analyse --configuration=phpstan/config.neon || vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr

@ -17,6 +17,7 @@
- `PluginInterface` added a deactivate (so plugin can stop whatever it is doing) and an uninstall (so the plugin can remove any files it created or do general cleanup) method. - `PluginInterface` added a deactivate (so plugin can stop whatever it is doing) and an uninstall (so the plugin can remove any files it created or do general cleanup) method.
- Plugins implementing `EventSubscriberInterface` will be deregistered from the EventDispatcher automatically when being deactivated, nothing to do there. - Plugins implementing `EventSubscriberInterface` will be deregistered from the EventDispatcher automatically when being deactivated, nothing to do there.
- `Pool` objects are now created via the `RepositorySet` class, you should use that in case you were using the `Pool` class directly. - `Pool` objects are now created via the `RepositorySet` class, you should use that in case you were using the `Pool` class directly.
- Custom installers extending from LibraryInstaller should be aware that in Composer 2 it MAY return PromiseInterface instances when calling parent::install/update/uninstall/installCode/removeCode. See [composer/installers](https://github.com/composer/installers/commit/5006d0c28730ade233a8f42ec31ac68fb1c5c9bb) for an example of how to handle this best.
- The `Composer\Installer` class changed quite a bit internally, but the inputs are almost the same: - The `Composer\Installer` class changed quite a bit internally, but the inputs are almost the same:
- `setAdditionalInstalledRepository` is now `setAdditionalFixedRepository` - `setAdditionalInstalledRepository` is now `setAdditionalFixedRepository`
- `setUpdateWhitelist` is now `setUpdateAllowList` - `setUpdateWhitelist` is now `setUpdateAllowList`

@ -27,12 +27,6 @@ parameters:
# BC with older PHPUnit # BC with older PHPUnit
- '~^Call to an undefined static method PHPUnit\\Framework\\TestCase::setExpectedException\(\)\.$~' - '~^Call to an undefined static method PHPUnit\\Framework\\TestCase::setExpectedException\(\)\.$~'
# hhvm should have support for $this in closures
-
count: 1
message: '~^Using \$this inside anonymous function is prohibited because of PHP 5\.3 support\.$~'
path: '../tests/Composer/Test/Repository/PlatformRepositoryTest.php'
paths: paths:
- ../src - ../src
- ../tests - ../tests

@ -38,6 +38,7 @@ use Symfony\Component\Finder\Finder;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Config\JsonConfigSource; use Composer\Config\JsonConfigSource;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Util\ProcessExecutor;
use Composer\Util\Loop; use Composer\Util\Loop;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
@ -186,7 +187,8 @@ EOT
$composer = Factory::create($io, null, $disablePlugins); $composer = Factory::create($io, null, $disablePlugins);
} }
$fs = new Filesystem(); $process = new ProcessExecutor($io);
$fs = new Filesystem($process);
if ($noScripts === false) { if ($noScripts === false) {
// dispatch event // dispatch event
@ -307,7 +309,8 @@ EOT
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
} }
$fs = new Filesystem(); $process = new ProcessExecutor($io);
$fs = new Filesystem($process);
if (!$fs->isAbsolutePath($directory)) { if (!$fs->isAbsolutePath($directory)) {
$directory = getcwd() . DIRECTORY_SEPARATOR . $directory; $directory = getcwd() . DIRECTORY_SEPARATOR . $directory;
} }
@ -397,11 +400,11 @@ EOT
$factory = new Factory(); $factory = new Factory();
$httpDownloader = $factory->createHttpDownloader($io, $config); $httpDownloader = $factory->createHttpDownloader($io, $config);
$dm = $factory->createDownloadManager($io, $config, $httpDownloader); $dm = $factory->createDownloadManager($io, $config, $httpDownloader, $process);
$dm->setPreferSource($preferSource) $dm->setPreferSource($preferSource)
->setPreferDist($preferDist); ->setPreferDist($preferDist);
$projectInstaller = new ProjectInstaller($directory, $dm); $projectInstaller = new ProjectInstaller($directory, $dm, $fs);
$im = $factory->createInstallationManager(new Loop($httpDownloader), $io); $im = $factory->createInstallationManager(new Loop($httpDownloader), $io);
$im->addInstaller($projectInstaller); $im->addInstaller($projectInstaller);
$im->execute(new InstalledFilesystemRepository(new JsonFile('php://memory')), array(new InstallOperation($package))); $im->execute(new InstalledFilesystemRepository(new JsonFile('php://memory')), array(new InstallOperation($package)));

@ -25,11 +25,17 @@ use React\Promise\PromiseInterface;
*/ */
class DownloadManager class DownloadManager
{ {
/** @var IOInterface */
private $io; private $io;
/** @var bool */
private $preferDist = false; private $preferDist = false;
/** @var bool */
private $preferSource = false; private $preferSource = false;
/** @var array<string, string> */
private $packagePreferences = array(); private $packagePreferences = array();
/** @var Filesystem */
private $filesystem; private $filesystem;
/** @var array<string, DownloaderInterface> */
private $downloaders = array(); private $downloaders = array();
/** /**

@ -39,16 +39,22 @@ use Composer\Downloader\TransportException;
*/ */
class FileDownloader implements DownloaderInterface, ChangeReportInterface class FileDownloader implements DownloaderInterface, ChangeReportInterface
{ {
/** @var IOInterface */
protected $io; protected $io;
/** @var Config */
protected $config; protected $config;
/** @var HttpDownloader */
protected $httpDownloader; protected $httpDownloader;
/** @var Filesystem */
protected $filesystem; protected $filesystem;
/** @var Cache */
protected $cache; protected $cache;
/** @var EventDispatcher */
protected $eventDispatcher;
/** /**
* @private this is only public for php 5.3 support in closures * @private this is only public for php 5.3 support in closures
*/ */
public $lastCacheWrites = array(); public $lastCacheWrites = array();
private $eventDispatcher;
/** /**
* Constructor. * Constructor.

@ -20,6 +20,7 @@ use Composer\Util\Platform;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
/** /**
* GZip archive downloader. * GZip archive downloader.
@ -31,10 +32,10 @@ class GzipDownloader extends ArchiveDownloader
/** @var ProcessExecutor */ /** @var ProcessExecutor */
protected $process; protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null) public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
{ {
$this->process = $process ?: new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache); parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
} }
protected function extract(PackageInterface $package, $file, $path) protected function extract(PackageInterface $package, $file, $path)

@ -18,10 +18,15 @@ use Composer\Package\PackageInterface;
use Composer\Package\Version\VersionGuesser; use Composer\Package\Version\VersionGuesser;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\IO\IOInterface;
use Composer\Config;
use Composer\Cache;
use Composer\Util\HttpDownloader;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\Filesystem as ComposerFilesystem; use Composer\Util\Filesystem;
use Composer\EventDispatcher\EventDispatcher;
use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
/** /**
* Download a package from a local path. * Download a package from a local path.
@ -34,6 +39,15 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
const STRATEGY_SYMLINK = 10; const STRATEGY_SYMLINK = 10;
const STRATEGY_MIRROR = 20; const STRATEGY_MIRROR = 20;
/** @var ProcessExecutor */
private $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
{
$this->process = $process ?: new ProcessExecutor($io);
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -115,7 +129,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
$allowedStrategies = array(self::STRATEGY_MIRROR); $allowedStrategies = array(self::STRATEGY_MIRROR);
} }
$fileSystem = new Filesystem(); $symfonyFilesystem = new SymfonyFilesystem();
$this->filesystem->removeDirectory($path); $this->filesystem->removeDirectory($path);
if ($output) { if ($output) {
@ -142,9 +156,9 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
$path = rtrim($path, "/"); $path = rtrim($path, "/");
$this->io->writeError(sprintf('Symlinking from %s', $url), false); $this->io->writeError(sprintf('Symlinking from %s', $url), false);
if ($transportOptions['relative']) { if ($transportOptions['relative']) {
$fileSystem->symlink($shortestPath, $path); $symfonyFilesystem->symlink($shortestPath, $path);
} else { } else {
$fileSystem->symlink($realUrl, $path); $symfonyFilesystem->symlink($realUrl, $path);
} }
} }
} catch (IOException $e) { } catch (IOException $e) {
@ -161,12 +175,11 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
// Fallback if symlink failed or if symlink is not allowed for the package // Fallback if symlink failed or if symlink is not allowed for the package
if (self::STRATEGY_MIRROR == $currentStrategy) { if (self::STRATEGY_MIRROR == $currentStrategy) {
$fs = new ComposerFilesystem(); $realUrl = $this->filesystem->normalizePath($realUrl);
$realUrl = $fs->normalizePath($realUrl);
$this->io->writeError(sprintf('%sMirroring from %s', $isFallback ? ' ' : '', $url), false); $this->io->writeError(sprintf('%sMirroring from %s', $isFallback ? ' ' : '', $url), false);
$iterator = new ArchivableFilesFinder($realUrl, array()); $iterator = new ArchivableFilesFinder($realUrl, array());
$fileSystem->mirror($realUrl, $path, $iterator); $symfonyFilesystem->mirror($realUrl, $path, $iterator);
} }
if ($output) { if ($output) {
@ -213,7 +226,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
public function getVcsReference(PackageInterface $package, $path) public function getVcsReference(PackageInterface $package, $path)
{ {
$parser = new VersionParser; $parser = new VersionParser;
$guesser = new VersionGuesser($this->config, new ProcessExecutor($this->io), $parser); $guesser = new VersionGuesser($this->config, $this->process, $parser);
$dumper = new ArrayDumper; $dumper = new ArrayDumper;
$packageConfig = $dumper->dump($package); $packageConfig = $dumper->dump($package);

@ -19,6 +19,7 @@ use Composer\Util\IniHelper;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\Util\Filesystem;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use RarArchive; use RarArchive;
@ -35,10 +36,10 @@ class RarDownloader extends ArchiveDownloader
/** @var ProcessExecutor */ /** @var ProcessExecutor */
protected $process; protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null) public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
{ {
$this->process = $process ?: new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache); parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
} }
protected function extract(PackageInterface $package, $file, $path) protected function extract(PackageInterface $package, $file, $path)

@ -65,7 +65,7 @@ class SvnDownloader extends VcsDownloader
throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
} }
$util = new SvnUtil($url, $this->io, $this->config); $util = new SvnUtil($url, $this->io, $this->config, $this->process);
$flags = ""; $flags = "";
if (version_compare($util->binaryVersion(), '1.7.0', '>=')) { if (version_compare($util->binaryVersion(), '1.7.0', '>=')) {
$flags .= ' --ignore-ancestry'; $flags .= ' --ignore-ancestry';
@ -103,7 +103,7 @@ class SvnDownloader extends VcsDownloader
*/ */
protected function execute(PackageInterface $package, $baseUrl, $command, $url, $cwd = null, $path = null) protected function execute(PackageInterface $package, $baseUrl, $command, $url, $cwd = null, $path = null)
{ {
$util = new SvnUtil($baseUrl, $this->io, $this->config); $util = new SvnUtil($baseUrl, $this->io, $this->config, $this->process);
$util->setCacheCredentials($this->cacheCredentials); $util->setCacheCredentials($this->cacheCredentials);
try { try {
return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose()); return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose());
@ -202,7 +202,7 @@ class SvnDownloader extends VcsDownloader
$command = sprintf('svn log -r%s:%s --incremental', ProcessExecutor::escape($fromRevision), ProcessExecutor::escape($toRevision)); $command = sprintf('svn log -r%s:%s --incremental', ProcessExecutor::escape($fromRevision), ProcessExecutor::escape($toRevision));
$util = new SvnUtil($baseUrl, $this->io, $this->config); $util = new SvnUtil($baseUrl, $this->io, $this->config, $this->process);
$util->setCacheCredentials($this->cacheCredentials); $util->setCacheCredentials($this->cacheCredentials);
try { try {
return $util->executeLocal($command, $path, null, $this->io->isVerbose()); return $util->executeLocal($command, $path, null, $this->io->isVerbose());

@ -19,6 +19,7 @@ use Composer\Package\PackageInterface;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
/** /**
* Xz archive downloader. * Xz archive downloader.
@ -31,11 +32,11 @@ class XzDownloader extends ArchiveDownloader
/** @var ProcessExecutor */ /** @var ProcessExecutor */
protected $process; protected $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null) public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
{ {
$this->process = $process ?: new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache); parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
} }
protected function extract(PackageInterface $package, $file, $path) protected function extract(PackageInterface $package, $file, $path)

@ -19,6 +19,7 @@ use Composer\Package\PackageInterface;
use Composer\Util\IniHelper; use Composer\Util\IniHelper;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\Filesystem;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\ExecutableFinder;
@ -38,10 +39,10 @@ class ZipDownloader extends ArchiveDownloader
/** @var ZipArchive|null */ /** @var ZipArchive|null */
private $zipArchiveObject; private $zipArchiveObject;
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null) public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
{ {
$this->process = $process ?: new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache); parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
} }
/** /**

@ -28,6 +28,7 @@ use Composer\Installer\PackageEvent;
use Composer\Installer\BinaryInstaller; use Composer\Installer\BinaryInstaller;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Script\Event as ScriptEvent; use Composer\Script\Event as ScriptEvent;
use Composer\ClassLoader;
use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\PhpExecutableFinder;
/** /**
@ -45,11 +46,17 @@ use Symfony\Component\Process\PhpExecutableFinder;
*/ */
class EventDispatcher class EventDispatcher
{ {
/** @var Composer */
protected $composer; protected $composer;
/** @var IOInterface */
protected $io; protected $io;
/** @var ?ClassLoader */
protected $loader; protected $loader;
/** @var ProcessExecutor */
protected $process; protected $process;
/** @var array<string, array<int, array<callable|string>>> */
protected $listeners = array(); protected $listeners = array();
/** @var list<string> */
private $eventStack; private $eventStack;
/** /**

@ -335,15 +335,16 @@ class Factory
} }
$httpDownloader = self::createHttpDownloader($io, $config); $httpDownloader = self::createHttpDownloader($io, $config);
$process = new ProcessExecutor($io);
$loop = new Loop($httpDownloader); $loop = new Loop($httpDownloader);
$composer->setLoop($loop); $composer->setLoop($loop);
// initialize event dispatcher // initialize event dispatcher
$dispatcher = new EventDispatcher($composer, $io); $dispatcher = new EventDispatcher($composer, $io, $process);
$composer->setEventDispatcher($dispatcher); $composer->setEventDispatcher($dispatcher);
// initialize repository manager // initialize repository manager
$rm = RepositoryFactory::manager($io, $config, $httpDownloader, $dispatcher); $rm = RepositoryFactory::manager($io, $config, $httpDownloader, $dispatcher, $process);
$composer->setRepositoryManager($rm); $composer->setRepositoryManager($rm);
// force-set the version of the global package if not defined as // force-set the version of the global package if not defined as
@ -354,7 +355,7 @@ class Factory
// load package // load package
$parser = new VersionParser; $parser = new VersionParser;
$guesser = new VersionGuesser($config, new ProcessExecutor($io), $parser); $guesser = new VersionGuesser($config, $process, $parser);
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io); $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io);
$package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd); $package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd);
$composer->setPackage($package); $composer->setPackage($package);
@ -368,7 +369,7 @@ class Factory
if ($fullLoad) { if ($fullLoad) {
// initialize download manager // initialize download manager
$dm = $this->createDownloadManager($io, $config, $httpDownloader, $dispatcher); $dm = $this->createDownloadManager($io, $config, $httpDownloader, $process, $dispatcher);
$composer->setDownloadManager($dm); $composer->setDownloadManager($dm);
// initialize autoload generator // initialize autoload generator
@ -381,7 +382,7 @@ class Factory
} }
// add installers to the manager (must happen after download manager is created since they read it out of $composer) // add installers to the manager (must happen after download manager is created since they read it out of $composer)
$this->createDefaultInstallers($im, $composer, $io); $this->createDefaultInstallers($im, $composer, $io, $process);
if ($fullLoad) { if ($fullLoad) {
$globalComposer = null; $globalComposer = null;
@ -399,7 +400,7 @@ class Factory
if ($fullLoad && isset($composerFile)) { if ($fullLoad && isset($composerFile)) {
$lockFile = self::getLockFile($composerFile); $lockFile = self::getLockFile($composerFile);
$locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $im, file_get_contents($composerFile)); $locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $im, file_get_contents($composerFile), $process);
$composer->setLocker($locker); $composer->setLocker($locker);
} }
@ -460,14 +461,16 @@ class Factory
* @param EventDispatcher $eventDispatcher * @param EventDispatcher $eventDispatcher
* @return Downloader\DownloadManager * @return Downloader\DownloadManager
*/ */
public function createDownloadManager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null) public function createDownloadManager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, ProcessExecutor $process, EventDispatcher $eventDispatcher = null)
{ {
$cache = null; $cache = null;
if ($config->get('cache-files-ttl') > 0) { if ($config->get('cache-files-ttl') > 0) {
$cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./'); $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
} }
$dm = new Downloader\DownloadManager($io); $fs = new Filesystem($process);
$dm = new Downloader\DownloadManager($io, false, $fs);
switch ($preferred = $config->get('preferred-install')) { switch ($preferred = $config->get('preferred-install')) {
case 'dist': case 'dist':
$dm->setPreferDist(true); $dm->setPreferDist(true);
@ -485,22 +488,19 @@ class Factory
$dm->setPreferences($preferred); $dm->setPreferences($preferred);
} }
$executor = new ProcessExecutor($io); $dm->setDownloader('git', new Downloader\GitDownloader($io, $config, $process, $fs));
$fs = new Filesystem($executor); $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config, $process, $fs));
$dm->setDownloader('fossil', new Downloader\FossilDownloader($io, $config, $process, $fs));
$dm->setDownloader('git', new Downloader\GitDownloader($io, $config, $executor, $fs)); $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config, $process, $fs));
$dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config, $executor, $fs)); $dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config, $process, $fs));
$dm->setDownloader('fossil', new Downloader\FossilDownloader($io, $config, $executor, $fs)); $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
$dm->setDownloader('hg', new Downloader\HgDownloader($io, $config, $executor, $fs)); $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
$dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config)); $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
$dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $executor)); $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
$dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $executor)); $dm->setDownloader('xz', new Downloader\XzDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache)); $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $executor)); $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
$dm->setDownloader('xz', new Downloader\XzDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $executor)); $dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache));
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache));
$dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache));
return $dm; return $dm;
} }
@ -544,10 +544,13 @@ class Factory
* @param Composer $composer * @param Composer $composer
* @param IO\IOInterface $io * @param IO\IOInterface $io
*/ */
protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io) protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io, ProcessExecutor $process = null)
{ {
$im->addInstaller(new Installer\LibraryInstaller($io, $composer, null)); $fs = new Filesystem($process);
$im->addInstaller(new Installer\PluginInstaller($io, $composer)); $binaryInstaller = new Installer\BinaryInstaller($io, rtrim($composer->getConfig()->get('bin-dir'), '/'), $composer->getConfig()->get('bin-compat'), $fs);
$im->addInstaller(new Installer\LibraryInstaller($io, $composer, null, $fs, $binaryInstaller));
$im->addInstaller(new Installer\PluginInstaller($io, $composer, $fs, $binaryInstaller));
$im->addInstaller(new Installer\MetapackageInstaller($io)); $im->addInstaller(new Installer\MetapackageInstaller($io));
} }

@ -26,6 +26,7 @@ use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
use Composer\EventDispatcher\EventDispatcher; use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\StreamContextFactory; use Composer\Util\StreamContextFactory;
use Composer\Util\Loop; use Composer\Util\Loop;
use React\Promise\PromiseInterface;
/** /**
* Package operation manager. * Package operation manager.
@ -36,11 +37,17 @@ use Composer\Util\Loop;
*/ */
class InstallationManager class InstallationManager
{ {
/** @var array<InstallerInterface> */
private $installers = array(); private $installers = array();
/** @var array<string, InstallerInterface> */
private $cache = array(); private $cache = array();
/** @var array<string, array<PackageInterface>> */
private $notifiablePackages = array(); private $notifiablePackages = array();
/** @var Loop */
private $loop; private $loop;
/** @var IOInterface */
private $io; private $io;
/** @var EventDispatcher */
private $eventDispatcher; private $eventDispatcher;
public function __construct(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null) public function __construct(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null)
@ -180,7 +187,7 @@ class InstallationManager
foreach ($cleanupPromises as $cleanup) { foreach ($cleanupPromises as $cleanup) {
$promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) { $promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) {
$promise = $cleanup(); $promise = $cleanup();
if (null === $promise) { if (!$promise instanceof PromiseInterface) {
$resolve(); $resolve();
} else { } else {
$promise->then(function () use ($resolve) { $promise->then(function () use ($resolve) {
@ -296,8 +303,8 @@ class InstallationManager
$io = $this->io; $io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage); $promise = $installer->prepare($opType, $package, $initialPackage);
if (null === $promise) { if (!$promise instanceof PromiseInterface) {
$promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); $promise = \React\Promise\resolve();
} }
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) { $promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {

@ -19,6 +19,7 @@ use Composer\Package\PackageInterface;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use Composer\Util\Platform; use Composer\Util\Platform;
use React\Promise\PromiseInterface;
/** /**
* Package installation manager. * Package installation manager.
@ -131,11 +132,19 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->binaryInstaller->removeBinaries($package); $this->binaryInstaller->removeBinaries($package);
} }
$this->installCode($package); $promise = $this->installCode($package);
$this->binaryInstaller->installBinaries($package, $this->getInstallPath($package)); if (!$promise instanceof PromiseInterface) {
if (!$repo->hasPackage($package)) { $promise = \React\Promise\resolve();
$repo->addPackage(clone $package);
} }
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($package);
return $promise->then(function () use ($binaryInstaller, $installPath, $package, $repo) {
$binaryInstaller->installBinaries($package, $installPath);
if (!$repo->hasPackage($package)) {
$repo->addPackage(clone $package);
}
});
} }
/** /**
@ -150,12 +159,20 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->initializeVendorDir(); $this->initializeVendorDir();
$this->binaryInstaller->removeBinaries($initial); $this->binaryInstaller->removeBinaries($initial);
$this->updateCode($initial, $target); $promise = $this->updateCode($initial, $target);
$this->binaryInstaller->installBinaries($target, $this->getInstallPath($target)); if (!$promise instanceof PromiseInterface) {
$repo->removePackage($initial); $promise = \React\Promise\resolve();
if (!$repo->hasPackage($target)) {
$repo->addPackage(clone $target);
} }
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($target);
return $promise->then(function () use ($binaryInstaller, $installPath, $target, $initial, $repo) {
$binaryInstaller->installBinaries($target, $installPath);
$repo->removePackage($initial);
if (!$repo->hasPackage($target)) {
$repo->addPackage(clone $target);
}
});
} }
/** /**
@ -167,17 +184,25 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
throw new \InvalidArgumentException('Package is not installed: '.$package); throw new \InvalidArgumentException('Package is not installed: '.$package);
} }
$this->removeCode($package); $promise = $this->removeCode($package);
$this->binaryInstaller->removeBinaries($package); if (!$promise instanceof PromiseInterface) {
$repo->removePackage($package); $promise = \React\Promise\resolve();
}
$binaryInstaller = $this->binaryInstaller;
$downloadPath = $this->getPackageBasePath($package); $downloadPath = $this->getPackageBasePath($package);
if (strpos($package->getName(), '/')) { $filesystem = $this->filesystem;
$packageVendorDir = dirname($downloadPath); return $promise->then(function () use ($binaryInstaller, $filesystem, $downloadPath, $package, $repo) {
if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) { $binaryInstaller->removeBinaries($package);
Silencer::call('rmdir', $packageVendorDir); $repo->removePackage($package);
if (strpos($package->getName(), '/')) {
$packageVendorDir = dirname($downloadPath);
if (is_dir($packageVendorDir) && $filesystem->isDirEmpty($packageVendorDir)) {
Silencer::call('rmdir', $packageVendorDir);
}
} }
} });
} }
/** /**
@ -227,7 +252,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
protected function installCode(PackageInterface $package) protected function installCode(PackageInterface $package)
{ {
$downloadPath = $this->getInstallPath($package); $downloadPath = $this->getInstallPath($package);
$this->downloadManager->install($package, $downloadPath); return $this->downloadManager->install($package, $downloadPath);
} }
protected function updateCode(PackageInterface $initial, PackageInterface $target) protected function updateCode(PackageInterface $initial, PackageInterface $target)
@ -240,21 +265,31 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath
|| substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath || substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath
) { ) {
$this->removeCode($initial); $promise = $this->removeCode($initial);
$this->installCode($target); if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
return; }
$self = $this;
return $promise->then(function () use ($self, $target) {
$reflMethod = new \ReflectionMethod($self, 'installCode');
$reflMethod->setAccessible(true);
// equivalent of $this->installCode($target) with php 5.3 support
// TODO remove this once 5.3 support is dropped
return $reflMethod->invoke($self, $target);
});
} }
$this->filesystem->rename($initialDownloadPath, $targetDownloadPath); $this->filesystem->rename($initialDownloadPath, $targetDownloadPath);
} }
$this->downloadManager->update($initial, $target, $targetDownloadPath); return $this->downloadManager->update($initial, $target, $targetDownloadPath);
} }
protected function removeCode(PackageInterface $package) protected function removeCode(PackageInterface $package)
{ {
$downloadPath = $this->getPackageBasePath($package); $downloadPath = $this->getPackageBasePath($package);
$this->downloadManager->remove($package, $downloadPath); return $this->downloadManager->remove($package, $downloadPath);
} }
protected function initializeVendorDir() protected function initializeVendorDir()

@ -16,6 +16,8 @@ use Composer\Composer;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use React\Promise\PromiseInterface;
/** /**
* Installer for plugin packages * Installer for plugin packages
@ -33,9 +35,9 @@ class PluginInstaller extends LibraryInstaller
* @param IOInterface $io * @param IOInterface $io
* @param Composer $composer * @param Composer $composer
*/ */
public function __construct(IOInterface $io, Composer $composer) public function __construct(IOInterface $io, Composer $composer, Filesystem $fs = null, BinaryInstaller $binaryInstaller = null)
{ {
parent::__construct($io, $composer, 'composer-plugin'); parent::__construct($io, $composer, 'composer-plugin', $fs, $binaryInstaller);
$this->installationManager = $composer->getInstallationManager(); $this->installationManager = $composer->getInstallationManager();
} }
@ -65,15 +67,20 @@ class PluginInstaller extends LibraryInstaller
*/ */
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
parent::install($repo, $package); $promise = parent::install($repo, $package);
try { if (!$promise instanceof PromiseInterface) {
$this->composer->getPluginManager()->registerPackage($package, true); $promise = \React\Promise\resolve();
} catch (\Exception $e) {
// Rollback installation
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package);
throw $e;
} }
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $package, $repo) {
try {
$pluginManager->registerPackage($package, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $package);
}
});
} }
/** /**
@ -81,22 +88,38 @@ class PluginInstaller extends LibraryInstaller
*/ */
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{ {
parent::update($repo, $initial, $target); $promise = parent::update($repo, $initial, $target);
if (!$promise instanceof PromiseInterface) {
try { $promise = \React\Promise\resolve();
$this->composer->getPluginManager()->deactivatePackage($initial, true);
$this->composer->getPluginManager()->registerPackage($target, true);
} catch (\Exception $e) {
// Rollback installation
$this->io->writeError('Plugin initialization failed, uninstalling plugin');
parent::uninstall($repo, $target);
throw $e;
} }
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $initial, $target, $repo) {
try {
$pluginManager->deactivatePackage($initial, true);
$pluginManager->registerPackage($target, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $target);
}
});
} }
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
$this->composer->getPluginManager()->uninstallPackage($package, true); $this->composer->getPluginManager()->uninstallPackage($package, true);
return parent::uninstall($repo, $package);
}
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
*/
public function rollbackInstall(\Exception $e, InstalledRepositoryInterface $repo, PackageInterface $package)
{
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package); parent::uninstall($repo, $package);
throw $e;
} }
} }

@ -29,11 +29,11 @@ class ProjectInstaller implements InstallerInterface
private $downloadManager; private $downloadManager;
private $filesystem; private $filesystem;
public function __construct($installPath, DownloadManager $dm) public function __construct($installPath, DownloadManager $dm, Filesystem $fs)
{ {
$this->installPath = rtrim(strtr($installPath, '\\', '/'), '/').'/'; $this->installPath = rtrim(strtr($installPath, '\\', '/'), '/').'/';
$this->downloadManager = $dm; $this->downloadManager = $dm;
$this->filesystem = new Filesystem; $this->filesystem = $fs;
} }
/** /**
@ -76,7 +76,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
{ {
$this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage); return $this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage);
} }
/** /**
@ -84,7 +84,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
{ {
$this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage); return $this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage);
} }
/** /**
@ -92,7 +92,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
$this->downloadManager->install($package, $this->installPath); return $this->downloadManager->install($package, $this->installPath);
} }
/** /**

@ -25,7 +25,9 @@ use Composer\Json\JsonFile;
*/ */
class ArchiveManager class ArchiveManager
{ {
/** @var DownloadManager */
protected $downloadManager; protected $downloadManager;
/** @var Loop */
protected $loop; protected $loop;
/** /**

@ -58,7 +58,7 @@ class RootPackageLoader extends ArrayLoader
$this->manager = $manager; $this->manager = $manager;
$this->config = $config; $this->config = $config;
$this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor(), $this->versionParser); $this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor($io), $this->versionParser);
$this->io = $io; $this->io = $io;
} }

@ -57,7 +57,7 @@ class Locker
* @param InstallationManager $installationManager installation manager instance * @param InstallationManager $installationManager installation manager instance
* @param string $composerFileContents The contents of the composer file * @param string $composerFileContents The contents of the composer file
*/ */
public function __construct(IOInterface $io, JsonFile $lockFile, InstallationManager $installationManager, $composerFileContents) public function __construct(IOInterface $io, JsonFile $lockFile, InstallationManager $installationManager, $composerFileContents, ProcessExecutor $process = null)
{ {
$this->lockFile = $lockFile; $this->lockFile = $lockFile;
$this->installationManager = $installationManager; $this->installationManager = $installationManager;
@ -65,7 +65,7 @@ class Locker
$this->contentHash = self::getContentHash($composerFileContents); $this->contentHash = self::getContentHash($composerFileContents);
$this->loader = new ArrayLoader(null, true); $this->loader = new ArrayLoader(null, true);
$this->dumper = new ArrayDumper(); $this->dumper = new ArrayDumper();
$this->process = new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
} }
/** /**

@ -34,15 +34,23 @@ use Composer\Util\PackageSorter;
*/ */
class PluginManager class PluginManager
{ {
/** @var Composer */
protected $composer; protected $composer;
/** @var IOInterface */
protected $io; protected $io;
/** @var Composer */
protected $globalComposer; protected $globalComposer;
/** @var VersionParser */
protected $versionParser; protected $versionParser;
/** @var bool */
protected $disablePlugins = false; protected $disablePlugins = false;
/** @var array<PluginInterface> */
protected $plugins = array(); protected $plugins = array();
/** @var array<string, PluginInterface> */
protected $registeredPlugins = array(); protected $registeredPlugins = array();
/** @var int */
private static $classCounter = 0; private static $classCounter = 0;
/** /**

@ -1155,12 +1155,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$retries = 3; $retries = 3;
if (isset($this->packagesNotFoundCache[$filename])) { if (isset($this->packagesNotFoundCache[$filename])) {
return new Promise(function ($resolve, $reject) { $resolve(array('packages' => array())); }); return \React\Promise\resolve(array('packages' => array()));
} }
if (isset($this->freshMetadataUrls[$filename]) && $lastModifiedTime) { if (isset($this->freshMetadataUrls[$filename]) && $lastModifiedTime) {
// make it look like we got a 304 response // make it look like we got a 304 response
return new Promise(function ($resolve, $reject) { $resolve(true); }); return \React\Promise\resolve(true);
} }
$httpDownloader = $this->httpDownloader; $httpDownloader = $this->httpDownloader;

@ -30,6 +30,7 @@ class PlatformRepository extends ArrayRepository
{ {
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD'; const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD';
private static $hhvmVersion;
private $versionParser; private $versionParser;
/** /**
@ -45,7 +46,7 @@ class PlatformRepository extends ArrayRepository
public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null) public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null)
{ {
$this->process = $process === null ? (new ProcessExecutor()) : $process; $this->process = $process;
foreach ($overrides as $name => $version) { foreach ($overrides as $name => $version) {
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version); $this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
} }
@ -251,22 +252,7 @@ class PlatformRepository extends ArrayRepository
$this->addPackage($lib); $this->addPackage($lib);
} }
$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null; if ($hhvmVersion = self::getHHVMVersion($this->process)) {
if ($hhvmVersion === null && !Platform::isWindows()) {
$finder = new ExecutableFinder();
$hhvm = $finder->find('hhvm');
if ($hhvm !== null) {
$exitCode = $this->process->execute(
ProcessExecutor::escape($hhvm).
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
$hhvmVersion
);
if ($exitCode !== 0) {
$hhvmVersion = null;
}
}
}
if ($hhvmVersion) {
try { try {
$prettyVersion = $hhvmVersion; $prettyVersion = $hhvmVersion;
$version = $this->versionParser->normalize($prettyVersion); $version = $this->versionParser->normalize($prettyVersion);
@ -362,4 +348,29 @@ class PlatformRepository extends ArrayRepository
{ {
return 'ext-' . str_replace(' ', '-', $name); return 'ext-' . str_replace(' ', '-', $name);
} }
private static function getHHVMVersion(ProcessExecutor $process = null)
{
if (null !== self::$hhvmVersion) {
return self::$hhvmVersion ?: null;
}
self::$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null;
if (self::$hhvmVersion === null && !Platform::isWindows()) {
self::$hhvmVersion = false;
$finder = new ExecutableFinder();
$hhvmPath = $finder->find('hhvm');
if ($hhvmPath !== null) {
$process = $process ?: new ProcessExecutor();
$exitCode = $process->execute(
ProcessExecutor::escape($hhvmPath).
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
self::$hhvmVersion
);
if ($exitCode !== 0) {
self::$hhvmVersion = false;
}
}
}
}
} }

@ -17,6 +17,7 @@ use Composer\IO\IOInterface;
use Composer\Config; use Composer\Config;
use Composer\EventDispatcher\EventDispatcher; use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\Util\ProcessExecutor;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
/** /**
@ -114,9 +115,9 @@ class RepositoryFactory
* @param HttpDownloader $httpDownloader * @param HttpDownloader $httpDownloader
* @return RepositoryManager * @return RepositoryManager
*/ */
public static function manager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null) public static function manager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, ProcessExecutor $process = null)
{ {
$rm = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher); $rm = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher, $process);
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository'); $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');

@ -17,6 +17,7 @@ use Composer\Config;
use Composer\EventDispatcher\EventDispatcher; use Composer\EventDispatcher\EventDispatcher;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Util\HttpDownloader; use Composer\Util\HttpDownloader;
use Composer\Util\ProcessExecutor;
/** /**
* Repositories manager. * Repositories manager.
@ -27,20 +28,30 @@ use Composer\Util\HttpDownloader;
*/ */
class RepositoryManager class RepositoryManager
{ {
/** @var InstalledRepositoryInterface */
private $localRepository; private $localRepository;
/** @var list<RepositoryInterface> */
private $repositories = array(); private $repositories = array();
/** @var array<string, string> */
private $repositoryClasses = array(); private $repositoryClasses = array();
/** @var IOInterface */
private $io; private $io;
/** @var Config */
private $config; private $config;
private $eventDispatcher; /** @var HttpDownloader */
private $httpDownloader; private $httpDownloader;
/** @var ?EventDispatcher */
private $eventDispatcher;
/** @var ProcessExecutor */
private $process;
public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null) public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, ProcessExecutor $process = null)
{ {
$this->io = $io; $this->io = $io;
$this->config = $config; $this->config = $config;
$this->httpDownloader = $httpDownloader; $this->httpDownloader = $httpDownloader;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->process = $process ?: new ProcessExecutor($io);
} }
/** /**
@ -130,7 +141,7 @@ class RepositoryManager
unset($config['only'], $config['exclude'], $config['canonical']); unset($config['only'], $config['exclude'], $config['canonical']);
} }
$repository = new $class($config, $this->io, $this->config, $this->httpDownloader, $this->eventDispatcher); $repository = new $class($config, $this->io, $this->config, $this->httpDownloader, $this->eventDispatcher, $this->process);
if (isset($filterConfig)) { if (isset($filterConfig)) {
$repository = new FilterRepository($repository, $filterConfig); $repository = new FilterRepository($repository, $filterConfig);

@ -223,7 +223,6 @@ class GitDriver extends VcsDriver
} }
$process = new ProcessExecutor($io); $process = new ProcessExecutor($io);
return $process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0; return $process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0;
} }
} }

@ -228,8 +228,8 @@ class HgDriver extends VcsDriver
return false; return false;
} }
$processExecutor = new ProcessExecutor($io); $process = new ProcessExecutor($io);
$exit = $processExecutor->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored); $exit = $process->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored);
return $exit === 0; return $exit === 0;
} }

@ -307,9 +307,8 @@ class SvnDriver extends VcsDriver
return false; return false;
} }
$processExecutor = new ProcessExecutor($io); $process = new ProcessExecutor($io);
$exit = $process->execute(
$exit = $processExecutor->execute(
"svn info --non-interactive ".ProcessExecutor::escape($url), "svn info --non-interactive ".ProcessExecutor::escape($url),
$ignoredOutput $ignoredOutput
); );
@ -320,14 +319,14 @@ class SvnDriver extends VcsDriver
} }
// Subversion client 1.7 and older // Subversion client 1.7 and older
if (false !== stripos($processExecutor->getErrorOutput(), 'authorization failed:')) { if (false !== stripos($process->getErrorOutput(), 'authorization failed:')) {
// This is likely a remote Subversion repository that requires // This is likely a remote Subversion repository that requires
// authentication. We will handle actual authentication later. // authentication. We will handle actual authentication later.
return true; return true;
} }
// Subversion client 1.8 and newer // Subversion client 1.8 and newer
if (false !== stripos($processExecutor->getErrorOutput(), 'Authentication failed')) { if (false !== stripos($process->getErrorOutput(), 'Authentication failed')) {
// This is likely a remote Subversion or newer repository that requires // This is likely a remote Subversion or newer repository that requires
// authentication. We will handle actual authentication later. // authentication. We will handle actual authentication later.
return true; return true;

@ -53,7 +53,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
private $emptyReferences = array(); private $emptyReferences = array();
private $versionTransportExceptions = array(); private $versionTransportExceptions = array();
public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $dispatcher = null, array $drivers = null, VersionCacheInterface $versionCache = null) public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $dispatcher = null, ProcessExecutor $process = null, array $drivers = null, VersionCacheInterface $versionCache = null)
{ {
parent::__construct(); parent::__construct();
$this->drivers = $drivers ?: array( $this->drivers = $drivers ?: array(
@ -78,7 +78,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$this->repoConfig = $repoConfig; $this->repoConfig = $repoConfig;
$this->versionCache = $versionCache; $this->versionCache = $versionCache;
$this->httpDownloader = $httpDownloader; $this->httpDownloader = $httpDownloader;
$this->processExecutor = new ProcessExecutor($io); $this->processExecutor = $process ?: new ProcessExecutor($io);
} }
public function getRepoName() public function getRepoName()

@ -28,7 +28,7 @@ class Filesystem
public function __construct(ProcessExecutor $executor = null) public function __construct(ProcessExecutor $executor = null)
{ {
$this->processExecutor = $executor ?: new ProcessExecutor(); $this->processExecutor = $executor;
} }
public function remove($file) public function remove($file)
@ -320,7 +320,7 @@ class Filesystem
if (Platform::isWindows()) { if (Platform::isWindows()) {
// Try to copy & delete - this is a workaround for random "Access denied" errors. // Try to copy & delete - this is a workaround for random "Access denied" errors.
$command = sprintf('xcopy %s %s /E /I /Q /Y', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $command = sprintf('xcopy %s %s /E /I /Q /Y', ProcessExecutor::escape($source), ProcessExecutor::escape($target));
$result = $this->processExecutor->execute($command, $output); $result = $this->getProcess()->execute($command, $output);
// clear stat cache because external processes aren't tracked by the php stat cache // clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache(); clearstatcache();
@ -334,7 +334,7 @@ class Filesystem
// We do not use PHP's "rename" function here since it does not support // We do not use PHP's "rename" function here since it does not support
// the case where $source, and $target are located on different partitions. // the case where $source, and $target are located on different partitions.
$command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target));
$result = $this->processExecutor->execute($command, $output); $result = $this->getProcess()->execute($command, $output);
// clear stat cache because external processes aren't tracked by the php stat cache // clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache(); clearstatcache();
@ -546,6 +546,10 @@ class Filesystem
*/ */
protected function getProcess() protected function getProcess()
{ {
if (!$this->processExecutor) {
$this->processExecutor = new ProcessExecutor();
}
return $this->processExecutor; return $this->processExecutor;
} }

@ -194,7 +194,7 @@ class ZipDownloaderTest extends TestCase
->method('execute') ->method('execute')
->will($this->returnValue(1)); ->will($this->returnValue(1));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }
@ -211,7 +211,7 @@ class ZipDownloaderTest extends TestCase
->method('execute') ->method('execute')
->will($this->returnValue(0)); ->will($this->returnValue(0));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }
@ -238,7 +238,7 @@ class ZipDownloaderTest extends TestCase
->method('extractTo') ->method('extractTo')
->will($this->returnValue(true)); ->will($this->returnValue(true));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }
@ -270,7 +270,7 @@ class ZipDownloaderTest extends TestCase
->method('extractTo') ->method('extractTo')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }
@ -298,7 +298,7 @@ class ZipDownloaderTest extends TestCase
->method('extractTo') ->method('extractTo')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }
@ -330,7 +330,7 @@ class ZipDownloaderTest extends TestCase
->method('extractTo') ->method('extractTo')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
} }

@ -23,6 +23,7 @@ use Composer\EventDispatcher\EventDispatcher;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Test\TestCase; use Composer\Test\TestCase;
use Composer\Util\Loop; use Composer\Util\Loop;
use Composer\Util\ProcessExecutor;
class FactoryMock extends Factory class FactoryMock extends Factory
{ {
@ -47,7 +48,7 @@ class FactoryMock extends Factory
return new InstallationManagerMock(); return new InstallationManagerMock();
} }
protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io) protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io, ProcessExecutor $process = null)
{ {
} }

@ -18,6 +18,7 @@ use Composer\Package\Archiver\ArchiveManager;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Util\Loop; use Composer\Util\Loop;
use Composer\Test\Mock\FactoryMock; use Composer\Test\Mock\FactoryMock;
use Composer\Util\ProcessExecutor;
class ArchiveManagerTest extends ArchiverTest class ArchiveManagerTest extends ArchiverTest
{ {
@ -36,7 +37,8 @@ class ArchiveManagerTest extends ArchiverTest
$dm = $factory->createDownloadManager( $dm = $factory->createDownloadManager(
$io = new NullIO, $io = new NullIO,
$config = FactoryMock::createConfig(), $config = FactoryMock::createConfig(),
$httpDownloader = $factory->createHttpDownloader($io, $config) $httpDownloader = $factory->createHttpDownloader($io, $config),
new ProcessExecutor($io)
); );
$loop = new Loop($httpDownloader); $loop = new Loop($httpDownloader);
$this->manager = $factory->createArchiveManager($factory->createConfig(), $dm, $loop); $this->manager = $factory->createArchiveManager($factory->createConfig(), $dm, $loop);

@ -14,11 +14,15 @@ namespace Composer\Test\Repository;
use Composer\Repository\PlatformRepository; use Composer\Repository\PlatformRepository;
use Composer\Test\TestCase; use Composer\Test\TestCase;
use Composer\Util\ProcessExecutor;
use Composer\Package\Version\VersionParser;
use Composer\Util\Platform; use Composer\Util\Platform;
use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\ExecutableFinder;
class PlatformRepositoryTest extends TestCase { class PlatformRepositoryTest extends TestCase
public function testHHVMVersionWhenExecutingInHHVM() { {
public function testHHVMVersionWhenExecutingInHHVM()
{
if (!defined('HHVM_VERSION_ID')) { if (!defined('HHVM_VERSION_ID')) {
$this->markTestSkipped('Not running with HHVM'); $this->markTestSkipped('Not running with HHVM');
return; return;
@ -36,7 +40,8 @@ class PlatformRepositoryTest extends TestCase {
); );
} }
public function testHHVMVersionWhenExecutingInPHP() { public function testHHVMVersionWhenExecutingInPHP()
{
if (defined('HHVM_VERSION_ID')) { if (defined('HHVM_VERSION_ID')) {
$this->markTestSkipped('Running with HHVM'); $this->markTestSkipped('Running with HHVM');
return; return;
@ -54,17 +59,18 @@ class PlatformRepositoryTest extends TestCase {
if ($hhvm === null) { if ($hhvm === null) {
$this->markTestSkipped('HHVM is not installed'); $this->markTestSkipped('HHVM is not installed');
} }
$process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock(); $repository = new PlatformRepository(array(), array());
$process->expects($this->once())->method('execute')->will($this->returnCallback(
function($command, &$out) {
$this->assertContains('HHVM_VERSION', $command);
$out = '4.0.1-dev';
return 0;
}
));
$repository = new PlatformRepository(array(), array(), $process);
$package = $repository->findPackage('hhvm', '*'); $package = $repository->findPackage('hhvm', '*');
$this->assertNotNull($package, 'failed to find HHVM package'); $this->assertNotNull($package, 'failed to find HHVM package');
$this->assertSame('4.0.1.0-dev', $package->getVersion());
$process = new ProcessExecutor();
$exitCode = $process->execute(
ProcessExecutor::escape($hhvm).
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
$version
);
$parser = new VersionParser;
$this->assertSame($parser->normalize($version), $package->getVersion());
} }
} }

Loading…
Cancel
Save