Merge branch 'master' of https://github.com/composer/composer into sort-suggestions

main
Niels Keurentjes 9 years ago
commit 990c07e4db

@ -12,7 +12,7 @@
* Added --strict to the `validate` command to treat any warning as an error that then returns a non-zero exit code
* Added a dependency on composer/semver, which is the externalized lib for all the version constraints parsing and handling
* Added support for classmap autoloading to load plugin classes and script handlers
* Added `bin-compat` config option that if set to `full` will create .bat proxy for binaries even if Compoesr runs in a linux VM
* Added `bin-compat` config option that if set to `full` will create .bat proxy for binaries even if Composer runs in a linux VM
* Added SPDX 2.0 support, and externalized that in a composer/spdx-licenses lib
* Added warnings when the classmap autoloader finds duplicate classes
* Added --file to the `archive` command to choose the filename

@ -45,7 +45,8 @@
}
},
"suggest": {
"ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic",
"ext-zip": "Enabling the zip extension allows you to unzip archives",
"ext-zlib": "Allow gzip compression of HTTP requests",
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
},
"autoload": {

2
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "fdf4b487fa59607376721ebec4ff4783",
"hash": "31b3c13c89f8d6c810637ca1fe8fc6ae",
"content-hash": "454148e20b837d9755dee7862f9c7a5d",
"packages": [
{

@ -109,7 +109,7 @@ mv composer.phar /usr/local/bin/composer
A quick copy-paste version including sudo:
```sh
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
curl -sS https://getcomposer.org/installer | sudo -H php -- --install-dir=/usr/local/bin --filename=composer
```
> **Note:** On some versions of OSX the `/usr` directory does not exist by

@ -413,7 +413,7 @@ If you have installed Composer for your entire system (see [global installation]
you may have to run the command with `root` privileges
```sh
sudo composer self-update
sudo -H composer self-update
```
### Options

@ -122,18 +122,25 @@ class ClassMapGenerator
$extraTypes .= '|enum';
}
try {
$contents = Silencer::call('php_strip_whitespace', $path);
if (!$contents) {
if (!file_exists($path)) {
throw new \Exception('File does not exist');
}
if (!is_readable($path)) {
throw new \Exception('File is not readable');
}
// Use @ here instead of Silencer to actively suppress 'unhelpful' output
// @link https://github.com/composer/composer/pull/4886
$contents = @php_strip_whitespace($path);
if (!$contents) {
if (!file_exists($path)) {
$message = 'File at "%s" does not exist, check your classmap definitions';
} elseif (!is_readable($path)) {
$message = 'File at "%s" is not readable, check its permissions';
} elseif ('' === trim(file_get_contents($path))) {
// The input file was really empty and thus contains no classes
return array();
} else {
$message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted';
}
$error = error_get_last();
if (isset($error['message'])) {
$message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message'];
}
} catch (\Exception $e) {
throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
throw new \RuntimeException(sprintf($message, $path));
}
// return early if there is no chance of matching anything in this file

@ -67,9 +67,7 @@ class Cache
{
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if ($this->enabled && file_exists($this->root . $file)) {
if ($this->io->isDebug()) {
$this->io->writeError('Reading '.$this->root . $file.' from cache');
}
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
return file_get_contents($this->root . $file);
}
@ -82,16 +80,12 @@ class Cache
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if ($this->io->isDebug()) {
$this->io->writeError('Writing '.$this->root . $file.' into cache');
}
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
try {
return file_put_contents($this->root . $file, $contents);
} catch (\ErrorException $e) {
if ($this->io->isDebug()) {
$this->io->writeError('<warning>Failed to write into cache: '.$e->getMessage().'</warning>');
}
$this->io->writeError('<warning>Failed to write into cache: '.$e->getMessage().'</warning>', true, IOInterface::DEBUG);
if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
// Remove partial file.
unlink($this->root . $file);
@ -152,9 +146,7 @@ class Cache
touch($this->root . $file);
}
if ($this->io->isDebug()) {
$this->io->writeError('Reading '.$this->root . $file.' from cache');
}
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
return copy($this->root . $file, $target);
}

@ -12,6 +12,7 @@
namespace Composer\Command;
use Composer\Util\Platform;
use Composer\Util\Silencer;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
@ -184,7 +185,7 @@ EOT
if ($input->getOption('editor')) {
$editor = escapeshellcmd(getenv('EDITOR'));
if (!$editor) {
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$editor = 'notepad';
} else {
foreach (array('vim', 'vi', 'nano', 'pico', 'ed') as $candidate) {
@ -197,7 +198,7 @@ EOT
}
$file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath();
system($editor . ' ' . $file . (defined('PHP_WINDOWS_VERSION_BUILD') ? '' : ' > `tty`'));
system($editor . ' ' . $file . (Platform::isWindows() ? '' : ' > `tty`'));
return 0;
}

@ -453,6 +453,10 @@ EOT
$errors['openssl'] = true;
}
if (extension_loaded('openssl') && OPENSSL_VERSION_NUMBER < 0x1000100f) {
$warnings['openssl_version'] = true;
}
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
$warnings['apc_cli'] = true;
}
@ -570,6 +574,15 @@ EOT
$text .= " Composer works with 5.3.2+ for most people, but there might be edge case issues.";
break;
case 'openssl_version':
// Attempt to parse version number out, fallback to whole string value.
$opensslVersion = strstr(trim(strstr(OPENSSL_VERSION_TEXT, ' ')), ' ', true);
$opensslVersion = $opensslVersion ?: OPENSSL_VERSION_TEXT;
$text = "The OpenSSL library ({$opensslVersion}) used by PHP does not support TLSv1.2 or TLSv1.1.".PHP_EOL;
$text .= "If possible you should upgrade OpenSSL to version 1.0.1 or above.";
break;
case 'xdebug_loaded':
$text = "The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
$text .= " Disabling it when using Composer is recommended.";

@ -16,6 +16,7 @@ use Composer\Factory;
use Composer\Package\CompletePackageInterface;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\ArrayRepository;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
@ -117,7 +118,7 @@ EOT
{
$url = ProcessExecutor::escape($url);
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
if (Platform::isWindows()) {
return passthru('start "web" explorer "' . $url . '"');
}

@ -88,9 +88,6 @@ EOT
if (!is_writable($tmpDir)) {
throw new FilesystemException('Composer update failed: the "'.$tmpDir.'" directory used to download the temp file could not be written');
}
if (!is_writable($localFilename)) {
throw new FilesystemException('Composer update failed: the "'.$localFilename.'" file could not be written');
}
if ($input->getOption('rollback')) {
return $this->rollback($output, $rollbackDir, $localFilename);
@ -271,10 +268,6 @@ TAGSPUBKEY
throw new \UnexpectedValueException('Composer rollback failed: no installation to roll back to in "'.$rollbackDir.'"');
}
if (!is_writable($rollbackDir)) {
throw new FilesystemException('Composer rollback failed: the "'.$rollbackDir.'" dir could not be written to');
}
$old = $rollbackDir . '/' . $rollbackVersion . self::OLD_INSTALL_EXT;
if (!is_file($old)) {

@ -20,6 +20,7 @@ use Composer\Semver\VersionParser;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Package\PackageInterface;
use Composer\Util\Platform;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
@ -232,7 +233,7 @@ EOT
// outside of a real terminal, use space without a limit
$width = PHP_INT_MAX;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$width--;
}
@ -246,10 +247,10 @@ EOT
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path') && ($nameLength + ($showVersion ? $versionLength : 0) + 24 <= $width);
foreach ($packages[$type] as $package) {
if (is_object($package)) {
$output->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
$io->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
if ($writeVersion) {
$output->write(' ' . str_pad($package->getFullPrettyVersion(), $versionLength, ' '), false);
$io->write(' ' . str_pad($package->getFullPrettyVersion(), $versionLength, ' '), false);
}
if ($writeDescription) {
@ -258,15 +259,15 @@ EOT
if (strlen($description) > $remaining) {
$description = substr($description, 0, $remaining - 3) . '...';
}
$output->write(' ' . $description);
$io->write(' ' . $description, false);
}
if ($writePath) {
$path = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n");
$output->write(' ' . $path);
$io->write(' ' . $path, false);
}
} else {
$output->write($indent . $package);
$io->write($indent . $package, false);
}
$io->write('');
}
@ -489,10 +490,10 @@ EOT
$packagesInTree = array();
$packagesInTree[] = $package;
$output->write(sprintf('<info>%s</info>', $package->getPrettyName()));
$output->write(' ' . $package->getPrettyVersion());
$output->write(' ' . strtok($package->getDescription(), "\r\n"));
$output->writeln('');
$io = $this->getIO();
$io->write(sprintf('<info>%s</info>', $package->getPrettyName()), false);
$io->write(' ' . $package->getPrettyVersion(), false);
$io->write(' ' . strtok($package->getDescription(), "\r\n"));
if (is_object($package)) {
$requires = $package->getRequires();

@ -12,6 +12,7 @@
namespace Composer\Console;
use Composer\Util\Platform;
use Composer\Util\Silencer;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\InputInterface;
@ -136,9 +137,7 @@ class Application extends BaseApplication
if ($newWorkDir = $this->getNewWorkingDir($input)) {
$oldWorkingDir = getcwd();
chdir($newWorkDir);
if ($io->isDebug() >= 4) {
$io->writeError('Changed CWD to ' . getcwd());
}
$io->writeError('Changed CWD to ' . getcwd(), true, IOInterface::DEBUG);
}
// add non-standard scripts as own commands
@ -214,21 +213,21 @@ class Application extends BaseApplication
|| (($df = disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|| (($df = disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
) {
$io->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
$io->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>', true, IOInterface::QUIET);
}
}
} catch (\Exception $e) {
}
Silencer::restore();
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
$io->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
if (Platform::isWindows() && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
$io->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>', true, IOInterface::QUIET);
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>', true, IOInterface::QUIET);
}
if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
$io->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
$io->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>', true, IOInterface::QUIET);
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>', true, IOInterface::QUIET);
}
}

@ -12,6 +12,8 @@
namespace Composer\DependencyResolver;
use Composer\Package\CompletePackage;
/**
* @author Nils Adermann <naderman@naderman.de>
*/
@ -203,25 +205,40 @@ class Rule
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
// handle php/hhvm
if (defined('HHVM_VERSION')) {
$text .= ' -> your HHVM version does not satisfy that requirement.';
return $text . ' -> your HHVM version does not satisfy that requirement.';
} elseif ($targetName === 'hhvm') {
$text .= ' -> you are running this with PHP and not HHVM.';
return $text . ' -> you are running this with PHP and not HHVM.';
} else {
$text .= ' -> your PHP version ('. phpversion() .') or value of "config.platform.php" in composer.json does not satisfy that requirement.';
$packages = $pool->whatProvides($targetName);
$package = count($packages) ? current($packages) : phpversion();
if (!($package instanceof CompletePackage)) {
return $text . ' -> your PHP version ('.phpversion().') does not satisfy that requirement.';
}
$extra = $package->getExtra();
if (!empty($extra['config.platform'])) {
$text .= ' -> your PHP version ('.phpversion().') overriden by "config.platform.php" version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
} else {
$text .= ' -> your PHP version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
}
return $text;
}
} elseif (0 === strpos($targetName, 'ext-')) {
// handle php extensions
$ext = substr($targetName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
$text .= ' -> the requested PHP extension '.$ext.' '.$error.'.';
return $text . ' -> the requested PHP extension '.$ext.' '.$error.'.';
} elseif (0 === strpos($targetName, 'lib-')) {
// handle linked libs
$lib = substr($targetName, 4);
$text .= ' -> the requested linked library '.$lib.' has the wrong version installed or is missing from your system, make sure to have the extension providing it.';
return $text . ' -> the requested linked library '.$lib.' has the wrong version installed or is missing from your system, make sure to have the extension providing it.';
} else {
$text .= ' -> no matching package found.';
return $text . ' -> no matching package found.';
}
}

@ -41,7 +41,7 @@ class SolverProblemsException extends \RuntimeException
}
if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) {
$text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
$text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
}
if ($hasExtensionProblems) {

@ -14,6 +14,7 @@ namespace Composer\Downloader;
use Composer\Package\PackageInterface;
use Symfony\Component\Finder\Finder;
use Composer\IO\IOInterface;
/**
* Base downloader for archives
@ -34,9 +35,7 @@ abstract class ArchiveDownloader extends FileDownloader
while ($retries--) {
$fileName = parent::download($package, $path);
if ($this->io->isVerbose()) {
$this->io->writeError(' Extracting archive');
}
$this->io->writeError(' Extracting archive', true, IOInterface::VERBOSE);
try {
$this->filesystem->ensureDirectoryExists($temporaryDir);

@ -141,9 +141,7 @@ class FileDownloader implements DownloaderInterface
if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) {
throw $e;
}
if ($this->io->isVerbose()) {
$this->io->writeError(' Download failed, retrying...');
}
$this->io->writeError(' Download failed, retrying...', true, IOInterface::VERBOSE);
usleep(500000);
}
}

@ -14,6 +14,7 @@ namespace Composer\Downloader;
use Composer\Package\PackageInterface;
use Composer\Util\Git as GitUtil;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
@ -43,7 +44,7 @@ class GitDownloader extends VcsDownloader
$path = $this->normalizePath($path);
$ref = $package->getSourceReference();
$flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : '';
$flag = Platform::isWindows() ? '/D ' : '';
$command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer';
$this->io->writeError(" Cloning ".$ref);
@ -353,7 +354,7 @@ class GitDownloader extends VcsDownloader
protected function normalizePath($path)
{
if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) {
if (Platform::isWindows() && strlen($path) > 0) {
$basePath = $path;
$removed = array();

@ -16,6 +16,7 @@ use Composer\Config;
use Composer\Cache;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Package\PackageInterface;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\IO\IOInterface;
@ -40,25 +41,26 @@ class GzipDownloader extends ArchiveDownloader
$targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3));
// Try to use gunzip on *nix
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
if (!Platform::isWindows()) {
$command = 'gzip -cd ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath);
if (0 === $this->process->execute($command, $ignoredOutput)) {
return;
}
if (extension_loaded('zlib')) {
// Fallback to using the PHP extension.
$this->extractUsingExt($file, $targetFilepath);
return;
}
$processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
throw new \RuntimeException($processError);
}
// Windows version of PHP has built-in support of gzip functions
$archiveFile = gzopen($file, 'rb');
$targetFile = fopen($targetFilepath, 'wb');
while ($string = gzread($archiveFile, 4096)) {
fwrite($targetFile, $string, strlen($string));
}
gzclose($archiveFile);
fclose($targetFile);
$this->extractUsingExt($file, $targetFilepath);
}
/**
@ -68,4 +70,15 @@ class GzipDownloader extends ArchiveDownloader
{
return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
}
private function extractUsingExt($file, $targetFilepath)
{
$archiveFile = gzopen($file, 'rb');
$targetFile = fopen($targetFilepath, 'wb');
while ($string = gzread($archiveFile, 4096)) {
fwrite($targetFile, $string, strlen($string));
}
gzclose($archiveFile);
fclose($targetFile);
}
}

@ -15,6 +15,7 @@ namespace Composer\Downloader;
use Composer\Config;
use Composer\Cache;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\IO\IOInterface;
@ -42,7 +43,7 @@ class RarDownloader extends ArchiveDownloader
$processError = null;
// Try to use unrar on *nix
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
if (!Platform::isWindows()) {
$command = 'unrar x ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path);
if (0 === $this->process->execute($command, $ignoredOutput)) {
@ -65,7 +66,7 @@ class RarDownloader extends ArchiveDownloader
$error = "Could not decompress the archive, enable the PHP rar extension or install unrar.\n"
. $iniMessage . "\n" . $processError;
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
if (!Platform::isWindows()) {
$error = "Could not decompress the archive, enable the PHP rar extension.\n" . $iniMessage;
}

@ -15,6 +15,7 @@ namespace Composer\Downloader;
use Composer\Config;
use Composer\Cache;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\IO\IOInterface;
@ -38,7 +39,7 @@ class ZipDownloader extends ArchiveDownloader
$processError = null;
// try to use unzip on *nix
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
if (!Platform::isWindows()) {
$command = 'unzip '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path);
try {
if (0 === $this->process->execute($command, $ignoredOutput)) {
@ -64,7 +65,7 @@ class ZipDownloader extends ArchiveDownloader
$error = "Could not decompress the archive, enable the PHP zip extension or install unzip.\n"
. $iniMessage . "\n" . $processError;
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
if (!Platform::isWindows()) {
$error = "Could not decompress the archive, enable the PHP zip extension.\n" . $iniMessage;
}

@ -155,9 +155,7 @@ class EventDispatcher
$event = $this->checkListenerExpectedEvent($callable, $event);
$return = false === call_user_func($callable, $event) ? 1 : 0;
} elseif ($this->isComposerScript($callable)) {
if ($this->io->isVerbose()) {
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable));
}
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE);
$scriptName = substr($callable, 1);
$args = $event->getArguments();
$flags = $event->getFlags();

@ -20,6 +20,7 @@ use Composer\Package\Version\VersionGuesser;
use Composer\Repository\RepositoryManager;
use Composer\Repository\WritableRepositoryInterface;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\Util\Silencer;
@ -51,7 +52,7 @@ class Factory
return $home;
}
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
if (Platform::isWindows()) {
if (!getenv('APPDATA')) {
throw new \RuntimeException('The APPDATA or COMPOSER_HOME environment variable must be set for composer to run correctly');
}
@ -90,7 +91,7 @@ class Factory
return $homeEnv . '/cache';
}
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
if (Platform::isWindows()) {
if ($cacheDir = getenv('LOCALAPPDATA')) {
$cacheDir .= '/Composer';
} else {
@ -125,7 +126,7 @@ class Factory
return $homeEnv;
}
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
if (Platform::isWindows()) {
return strtr($home, '\\', '/');
}
@ -190,6 +191,20 @@ class Factory
}
$config->setAuthConfigSource(new JsonConfigSource($file, true));
// load COMPOSER_AUTH environment variable if set
if ($composerAuthEnv = getenv('COMPOSER_AUTH')) {
$authData = json_decode($composerAuthEnv, true);
if (is_null($authData)) {
throw new \UnexpectedValueException('COMPOSER_AUTH environment variable is malformed, should be a valid JSON object');
}
if ($io && $io->isDebug()) {
$io->writeError('Loading auth config from COMPOSER_AUTH');
}
$config->merge(array('config' => $authData));
}
return $config;
}
@ -293,14 +308,10 @@ class Factory
$config = static::createConfig($io, $cwd);
$config->merge($localConfig);
if (isset($composerFile)) {
if ($io && $io->isDebug()) {
$io->writeError('Loading config file ' . $composerFile);
}
$io->writeError('Loading config file ' . $composerFile, true, IOInterface::DEBUG);
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
if ($localAuthFile->exists()) {
if ($io && $io->isDebug()) {
$io->writeError('Loading config file ' . $localAuthFile->getPath());
}
$io->writeError('Loading config file ' . $localAuthFile->getPath(), true, IOInterface::DEBUG);
$config->merge(array('config' => $localAuthFile->read()));
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
}
@ -435,9 +446,7 @@ class Factory
try {
$composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
} catch (\Exception $e) {
if ($io->isDebug()) {
$io->writeError('Failed to initialize global composer: '.$e->getMessage());
}
$io->writeError('Failed to initialize global composer: '.$e->getMessage(), true, IOInterface::DEBUG);
}
return $composer;
@ -503,6 +512,7 @@ class Factory
}
$am = new Archiver\ArchiveManager($dm);
$am->addArchiver(new Archiver\ZipArchiver);
$am->addArchiver(new Archiver\PharArchiver);
return $am;

@ -60,50 +60,25 @@ abstract class BaseIO implements IOInterface
*/
public function loadConfiguration(Config $config)
{
$githubOauth = $config->get('github-oauth');
$gitlabOauth = $config->get('gitlab-oauth');
$httpBasic = $config->get('http-basic');
// Use COMPOSER_AUTH environment variable if set
if ($composerAuthEnv = getenv('COMPOSER_AUTH')) {
$authData = json_decode($composerAuthEnv, true);
if (is_null($authData)) {
throw new \UnexpectedValueException('COMPOSER_AUTH environment variable is malformed');
}
if (isset($authData['github-oauth'])) {
$githubOauth = array_merge($githubOauth, $authData['github-oauth']);
}
if (isset($authData['gitlab-oauth'])) {
$gitlabOauth = array_merge($gitlabOauth, $authData['gitlab-oauth']);
}
if (isset($authData['http-basic'])) {
$httpBasic = array_merge($httpBasic, $authData['http-basic']);
}
}
$githubOauth = $config->get('github-oauth') ?: array();
$gitlabOauth = $config->get('gitlab-oauth') ?: array();
$httpBasic = $config->get('http-basic') ?: array();
// reload oauth token from config if available
if ($githubOauth) {
foreach ($githubOauth as $domain => $token) {
if (!preg_match('{^[a-z0-9]+$}', $token)) {
throw new \UnexpectedValueException('Your github oauth token for '.$domain.' contains invalid characters: "'.$token.'"');
}
$this->setAuthentication($domain, $token, 'x-oauth-basic');
foreach ($githubOauth as $domain => $token) {
if (!preg_match('{^[a-z0-9]+$}', $token)) {
throw new \UnexpectedValueException('Your github oauth token for '.$domain.' contains invalid characters: "'.$token.'"');
}
$this->setAuthentication($domain, $token, 'x-oauth-basic');
}
if ($gitlabOauth) {
foreach ($gitlabOauth as $domain => $token) {
$this->setAuthentication($domain, $token, 'oauth2');
}
foreach ($gitlabOauth as $domain => $token) {
$this->setAuthentication($domain, $token, 'oauth2');
}
// reload http basic credentials from config if available
if ($httpBasic) {
foreach ($httpBasic as $domain => $cred) {
$this->setAuthentication($domain, $cred['username'], $cred['password']);
}
foreach ($httpBasic as $domain => $cred) {
$this->setAuthentication($domain, $cred['username'], $cred['password']);
}
// setup process timeout

@ -35,7 +35,7 @@ class BufferIO extends ConsoleIO
$input = new StringInput($input);
$input->setInteractive(false);
$output = new StreamOutput(fopen('php://memory', 'rw'), $verbosity, !empty($formatter), $formatter);
$output = new StreamOutput(fopen('php://memory', 'rw'), $verbosity, $formatter ? $formatter->isDecorated() : false, $formatter);
parent::__construct($input, $output, new HelperSet(array()));
}

@ -33,6 +33,7 @@ class ConsoleIO extends BaseIO
protected $lastMessage;
protected $lastMessageErr;
private $startTime;
private $verbosityMap;
/**
* Constructor.
@ -46,6 +47,13 @@ class ConsoleIO extends BaseIO
$this->input = $input;
$this->output = $output;
$this->helperSet = $helperSet;
$this->verbosityMap = array(
self::QUIET => OutputInterface::VERBOSITY_QUIET,
self::NORMAL => OutputInterface::VERBOSITY_NORMAL,
self::VERBOSE => OutputInterface::VERBOSITY_VERBOSE,
self::VERY_VERBOSE => OutputInterface::VERBOSITY_VERY_VERBOSE,
self::DEBUG => OutputInterface::VERBOSITY_DEBUG,
);
}
public function enableDebugging($startTime)
@ -96,26 +104,32 @@ class ConsoleIO extends BaseIO
/**
* {@inheritDoc}
*/
public function write($messages, $newline = true)
public function write($messages, $newline = true, $verbosity = self::NORMAL)
{
$this->doWrite($messages, $newline, false);
$this->doWrite($messages, $newline, false, $verbosity);
}
/**
* {@inheritDoc}
*/
public function writeError($messages, $newline = true)
public function writeError($messages, $newline = true, $verbosity = self::NORMAL)
{
$this->doWrite($messages, $newline, true);
$this->doWrite($messages, $newline, true, $verbosity);
}
/**
* @param array|string $messages
* @param bool $newline
* @param bool $stderr
* @param int $verbosity
*/
private function doWrite($messages, $newline, $stderr)
private function doWrite($messages, $newline, $stderr, $verbosity)
{
$sfVerbosity = $this->verbosityMap[$verbosity];
if ($sfVerbosity > $this->output->getVerbosity()) {
return;
}
if (null !== $this->startTime) {
$memoryUsage = memory_get_usage() / 1024 / 1024;
$timeSpent = microtime(true) - $this->startTime;
@ -125,30 +139,30 @@ class ConsoleIO extends BaseIO
}
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
$this->output->getErrorOutput()->write($messages, $newline);
$this->output->getErrorOutput()->write($messages, $newline, $sfVerbosity);
$this->lastMessageErr = join($newline ? "\n" : '', (array) $messages);
return;
}
$this->output->write($messages, $newline);
$this->output->write($messages, $newline, $sfVerbosity);
$this->lastMessage = join($newline ? "\n" : '', (array) $messages);
}
/**
* {@inheritDoc}
*/
public function overwrite($messages, $newline = true, $size = null)
public function overwrite($messages, $newline = true, $size = null, $verbosity = self::NORMAL)
{
$this->doOverwrite($messages, $newline, $size, false);
$this->doOverwrite($messages, $newline, $size, false, $verbosity);
}
/**
* {@inheritDoc}
*/
public function overwriteError($messages, $newline = true, $size = null)
public function overwriteError($messages, $newline = true, $size = null, $verbosity = self::NORMAL)
{
$this->doOverwrite($messages, $newline, $size, true);
$this->doOverwrite($messages, $newline, $size, true, $verbosity);
}
/**
@ -156,8 +170,9 @@ class ConsoleIO extends BaseIO
* @param bool $newline
* @param int|null $size
* @param bool $stderr
* @param int $verbosity
*/
private function doOverwrite($messages, $newline, $size, $stderr)
private function doOverwrite($messages, $newline, $size, $stderr, $verbosity)
{
// messages can be an array, let's convert it to string anyway
$messages = join($newline ? "\n" : '', (array) $messages);
@ -168,21 +183,21 @@ class ConsoleIO extends BaseIO
$size = strlen(strip_tags($stderr ? $this->lastMessageErr : $this->lastMessage));
}
// ...let's fill its length with backspaces
$this->doWrite(str_repeat("\x08", $size), false, $stderr);
$this->doWrite(str_repeat("\x08", $size), false, $stderr, $verbosity);
// write the new message
$this->doWrite($messages, false, $stderr);
$this->doWrite($messages, false, $stderr, $verbosity);
$fill = $size - strlen(strip_tags($messages));
if ($fill > 0) {
// whitespace whatever has left
$this->doWrite(str_repeat(' ', $fill), false, $stderr);
$this->doWrite(str_repeat(' ', $fill), false, $stderr, $verbosity);
// move the cursor back
$this->doWrite(str_repeat("\x08", $fill), false, $stderr);
$this->doWrite(str_repeat("\x08", $fill), false, $stderr, $verbosity);
}
if ($newline) {
$this->doWrite('', true, $stderr);
$this->doWrite('', true, $stderr, $verbosity);
}
if ($stderr) {

@ -21,6 +21,12 @@ use Composer\Config;
*/
interface IOInterface
{
const QUIET = 1;
const NORMAL = 2;
const VERBOSE = 4;
const VERY_VERBOSE = 8;
const DEBUG = 16;
/**
* Is this input means interactive?
*
@ -59,36 +65,40 @@ interface IOInterface
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
*/
public function write($messages, $newline = true);
public function write($messages, $newline = true, $verbosity = self::NORMAL);
/**
* Writes a message to the error output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
*/
public function writeError($messages, $newline = true);
public function writeError($messages, $newline = true, $verbosity = self::NORMAL);
/**
* Overwrites a previous message to the output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $size The size of line
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $size The size of line
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
*/
public function overwrite($messages, $newline = true, $size = null);
public function overwrite($messages, $newline = true, $size = null, $verbosity = self::NORMAL);
/**
* Overwrites a previous message to the error output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $size The size of line
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param int $size The size of line
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
*/
public function overwriteError($messages, $newline = true, $size = null);
public function overwriteError($messages, $newline = true, $size = null, $verbosity = self::NORMAL);
/**
* Asks a question to the user.

@ -62,28 +62,28 @@ class NullIO extends BaseIO
/**
* {@inheritDoc}
*/
public function write($messages, $newline = true)
public function write($messages, $newline = true, $verbosity = self::NORMAL)
{
}
/**
* {@inheritDoc}
*/
public function writeError($messages, $newline = true)
public function writeError($messages, $newline = true, $verbosity = self::NORMAL)
{
}
/**
* {@inheritDoc}
*/
public function overwrite($messages, $newline = true, $size = 80)
public function overwrite($messages, $newline = true, $size = 80, $verbosity = self::NORMAL)
{
}
/**
* {@inheritDoc}
*/
public function overwriteError($messages, $newline = true, $size = 80)
public function overwriteError($messages, $newline = true, $size = 80, $verbosity = self::NORMAL)
{
}

@ -529,10 +529,8 @@ class Installer
return max(1, $e->getCode());
}
if ($this->io->isVerbose()) {
$this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies");
$this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies");
}
$this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE);
$this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies", true, IOInterface::VERBOSE);
// force dev packages to be updated if we update or install from a (potentially new) lock
$operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, 'force-updates', $operations);
@ -578,10 +576,8 @@ class Installer
&& (!$operation->getTargetPackage()->getSourceReference() || $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference())
&& (!$operation->getTargetPackage()->getDistReference() || $operation->getTargetPackage()->getDistReference() === $operation->getInitialPackage()->getDistReference())
) {
if ($this->io->isDebug()) {
$this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
$this->io->writeError('');
}
$this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version', true, IOInterface::DEBUG);
$this->io->writeError('', true, IOInterface::DEBUG);
continue;
}
@ -1199,6 +1195,7 @@ class Installer
foreach ($requirePackages as $requirePackage) {
if (isset($skipPackages[$requirePackage->getName()])) {
$this->io->writeError('<warning>Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>');
continue;
}
$packageQueue->enqueue($requirePackage);

@ -17,6 +17,7 @@ use Composer\IO\IOInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\Silencer;
@ -241,7 +242,7 @@ class LibraryInstaller implements InstallerInterface
}
if ($this->binCompat === "auto") {
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$this->installFullBinaries($binPath, $link, $bin, $package);
} else {
$this->installSymlinkBinaries($binPath, $link);

@ -17,6 +17,7 @@ use Composer\Composer;
use Composer\Downloader\PearPackageExtractor;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
/**
@ -53,7 +54,7 @@ class PearInstaller extends LibraryInstaller
parent::installCode($package);
parent::initializeBinDir();
$isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
$isWindows = Platform::isWindows();
$php_bin = $this->binDir . ($isWindows ? '/composer-php.bat' : '/composer-php');
if (!$isWindows) {
@ -75,9 +76,7 @@ class PearInstaller extends LibraryInstaller
$pearExtractor = new PearPackageExtractor($packageArchive);
$pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin', 'data' => '/data'), $vars);
if ($this->io->isVerbose()) {
$this->io->writeError(' Cleaning up');
}
$this->io->writeError(' Cleaning up', true, IOInterface::VERBOSE);
$this->filesystem->unlink($packageArchive);
}

@ -0,0 +1,65 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Package\Archiver;
use ZipArchive;
/**
* @author Jan Prieser <jan@prieser.net>
*/
class ZipArchiver implements ArchiverInterface
{
protected static $formats = array(
'zip' => 1
);
/**
* {@inheritdoc}
*/
public function archive($sources, $target, $format, array $excludes = array())
{
$sources = realpath($sources);
$zip = new ZipArchive();
$res = $zip->open($target, ZipArchive::CREATE);
if ($res === true) {
$files = new ArchivableFilesFinder($sources, $excludes);
foreach($files as $file) {
/** @var $file \SplFileInfo */
$filepath = $file->getPath()."/".$file->getFilename();
$localname = str_replace($sources."/", '', $filepath);
$zip->addFile($filepath, $localname);
}
if ($zip->close()) {
return $target;
}
}
$message = sprintf("Could not create archive '%s' from '%s': %s",
$target,
$sources,
$zip->getStatusString()
);
throw new \RuntimeException($message);
}
/**
* {@inheritdoc}
*/
public function supports($format, $sourceType)
{
return isset(static::$formats[$format]) && $this->compressionAvailable();
}
private function compressionAvailable() {
return class_exists('ZipArchive');
}
}

@ -113,6 +113,11 @@ class RootPackageLoader extends ArrayLoader
}
}
if (isset($links[$config['name']])) {
throw new \InvalidArgumentException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL .
'Did you accidentally name your root package after an external package?', $config['name']));
}
$realPackage->setAliases($aliases);
$realPackage->setStabilityFlags($stabilityFlags);
$realPackage->setReferences($references);

@ -206,9 +206,7 @@ class PluginManager
*/
private function addPlugin(PluginInterface $plugin)
{
if ($this->io->isDebug()) {
$this->io->writeError('Loading plugin '.get_class($plugin));
}
$this->io->writeError('Loading plugin '.get_class($plugin), true, IOInterface::DEBUG);
$this->plugins[] = $plugin;
$plugin->activate($this->composer, $this->io);

@ -67,16 +67,12 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
$package = $this->getComposerInformation($file);
if (!$package) {
if ($io->isVerbose()) {
$io->writeError("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package");
}
$io->writeError("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package", true, IOInterface::VERBOSE);
continue;
}
if ($io->isVerbose()) {
$template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>';
$io->writeError(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()));
}
$template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>';
$io->writeError(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()), true, IOInterface::VERBOSE);
$this->addPackage($package);
}

@ -125,17 +125,16 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
$package['dist'] = array(
'type' => 'path',
'url' => $url,
'reference' => '',
'reference' => sha1($json),
);
if (!isset($package['version'])) {
$package['version'] = $this->versionGuesser->guessVersion($package, $path) ?: 'dev-master';
}
$output = '';
if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
$package['dist']['reference'] = trim($output);
} else {
$package['dist']['reference'] = Locker::getContentHash($json);
}
$package = $this->loader->load($package);

@ -105,9 +105,7 @@ class PearRepository extends ArrayRepository implements ConfigurableRepositoryIn
try {
$normalizedVersion = $versionParser->normalize($version);
} catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) {
$this->io->writeError('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage());
}
$this->io->writeError('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage(), true, IOInterface::VERBOSE);
continue;
}

@ -59,6 +59,7 @@ class PlatformRepository extends ArrayRepository
$version = $versionParser->normalize($override['version']);
$package = new CompletePackage($override['name'], $version, $override['version']);
$package->setDescription('Package overridden via config.platform');
$package->setExtra(array('config.platform' => true));
parent::addPackage($package);
}
@ -114,6 +115,7 @@ class PlatformRepository extends ArrayRepository
// relying on them.
foreach ($loadedExtensions as $name) {
$prettyVersion = null;
$description = 'The '.$name.' PHP library';
switch ($name) {
case 'curl':
$curlVersion = curl_version();
@ -146,9 +148,27 @@ class PlatformRepository extends ArrayRepository
break;
case 'openssl':
$prettyVersion = preg_replace_callback('{^(?:OpenSSL\s*)?([0-9.]+)([a-z]?).*}', function ($match) {
return $match[1] . (empty($match[2]) ? '' : '.'.(ord($match[2]) - 96));
$prettyVersion = preg_replace_callback('{^(?:OpenSSL\s*)?([0-9.]+)([a-z]*).*}', function ($match) {
if (empty($match[2])) {
return $match[1];
}
// OpenSSL versions add another letter when they reach Z.
// e.g. OpenSSL 0.9.8zh 3 Dec 2015
if (!preg_match('{^z*[a-z]$}', $match[2])) {
// 0.9.8abc is garbage
return 0;
}
$len = strlen($match[2]);
$patchVersion = ($len - 1) * 26; // All Z
$patchVersion += ord($match[2][$len - 1]) - 96;
return $match[1].'.'.$patchVersion;
}, OPENSSL_VERSION_TEXT);
$description = OPENSSL_VERSION_TEXT;
break;
case 'pcre':
@ -175,7 +195,7 @@ class PlatformRepository extends ArrayRepository
}
$lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
$lib->setDescription('The '.$name.' PHP library');
$lib->setDescription($description);
$this->addPackage($lib);
}

@ -160,9 +160,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
}
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->writeError('Skipping Bitbucket git driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
$io->writeError('Skipping Bitbucket git driver for '.$url.' because the OpenSSL PHP extension is missing.', true, IOInterface::VERBOSE);
return false;
}

@ -268,9 +268,7 @@ class GitHubDriver extends VcsDriver
}
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->writeError('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
$io->writeError('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.', true, IOInterface::VERBOSE);
return false;
}

@ -367,9 +367,7 @@ class GitLabDriver extends VcsDriver
}
if ('https' === $scheme && !extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->write('Skipping GitLab driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
$io->writeError('Skipping GitLab driver for '.$url.' because the OpenSSL PHP extension is missing.', true, IOInterface::VERBOSE);
return false;
}

@ -170,9 +170,7 @@ class HgBitbucketDriver extends VcsDriver
}
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->writeError('Skipping Bitbucket hg driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
$io->writeError('Skipping Bitbucket hg driver for '.$url.' because the OpenSSL PHP extension is missing.', true, IOInterface::VERBOSE);
return false;
}

@ -110,7 +110,7 @@ class Filesystem
return $this->removeDirectoryPhp($directory);
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory)));
} else {
$cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory));
@ -181,10 +181,10 @@ class Filesystem
{
if (!@$this->unlinkImplementation($path)) {
// retry after a bit on windows since it tends to be touchy with mass removals
if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@$this->unlinkImplementation($path))) {
if (!Platform::isWindows() || (usleep(350000) && !@$this->unlinkImplementation($path))) {
$error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message'];
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
}
@ -206,10 +206,10 @@ class Filesystem
{
if (!@rmdir($path)) {
// retry after a bit on windows since it tends to be touchy with mass removals
if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@rmdir($path))) {
if (!Platform::isWindows() || (usleep(350000) && !@rmdir($path))) {
$error = error_get_last();
$message = 'Could not delete '.$path.': ' . @$error['message'];
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
}
@ -264,7 +264,7 @@ class Filesystem
return $this->copyThenRemove($source, $target);
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
// 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));
$result = $this->processExecutor->execute($command, $output);
@ -460,7 +460,7 @@ class Filesystem
public static function getPlatformPath($path)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$path = preg_replace('{^(?:file:///([a-z])/)}i', 'file://$1:/', $path);
}
@ -498,7 +498,7 @@ class Filesystem
*/
private function unlinkImplementation($path)
{
if (defined('PHP_WINDOWS_VERSION_BUILD') && is_dir($path) && is_link($path)) {
if (Platform::isWindows() && is_dir($path) && is_link($path)) {
return rmdir($path);
}

@ -51,10 +51,7 @@ class Perforce
public static function create($repoConfig, $port, $path, ProcessExecutor $process, IOInterface $io)
{
$isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
$perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io);
return $perforce;
return new Perforce($repoConfig, $port, $path, $process, Platform::isWindows(), $io);
}
public static function checkServerExists($url, ProcessExecutor $processExecutor)

@ -0,0 +1,28 @@
<?php
/**
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Util;
/**
* Platform helper for uniform platform-specific tests.
*
* @author Niels Keurentjes <niels.keurentjes@omines.com>
*/
class Platform
{
/**
* @return bool Whether the host machine is running a Windows OS
*/
public static function isWindows()
{
return defined('PHP_WINDOWS_VERSION_BUILD');
}
}

@ -50,7 +50,7 @@ class ProcessExecutor
// make sure that null translate to the proper directory in case the dir is a symlink
// and we call a git command, because msysgit does not handle symlinks properly
if (null === $cwd && defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($command, 'git') && getcwd()) {
if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) {
$cwd = realpath(getcwd());
}

@ -227,9 +227,7 @@ class RemoteFilesystem
unset($tempAdditionalOptions);
$userlandFollow = isset($options['http']['follow_location']) && !$options['http']['follow_location'];
if ($this->io->isDebug()) {
$this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
}
$this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl, true, IOInterface::DEBUG);
if (isset($options['github-token'])) {
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
@ -609,13 +607,11 @@ class RemoteFilesystem
// Handle subjectAltName on lesser PHP's.
$certMap = $this->peerCertificateMap[$urlAuthority];
if ($this->io->isDebug()) {
$this->io->writeError(sprintf(
'Using <info>%s</info> as CN for subjectAltName enabled host <info>%s</info>',
$certMap['cn'],
$urlAuthority
));
}
$this->io->writeError(sprintf(
'Using <info>%s</info> as CN for subjectAltName enabled host <info>%s</info>',
$certMap['cn'],
$urlAuthority
), true, IOInterface::DEBUG);
$tlsOptions['ssl']['CN_match'] = $certMap['cn'];
$tlsOptions['ssl']['peer_fingerprint'] = $certMap['fp'];
@ -689,9 +685,7 @@ class RemoteFilesystem
if (!empty($targetUrl)) {
$this->redirects++;
if ($this->io->isDebug()) {
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $targetUrl));
}
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $targetUrl), true, IOInterface::DEBUG);
$additionalOptions['redirects'] = $this->redirects;
@ -914,9 +908,7 @@ class RemoteFilesystem
return $files[$filename];
}
if ($this->io->isDebug()) {
$this->io->writeError('Checking CA file '.realpath($filename));
}
$this->io->writeError('Checking CA file '.realpath($filename), true, IOInterface::DEBUG);
$contents = file_get_contents($filename);
// assume the CA is valid if php is vulnerable to

@ -175,7 +175,7 @@ final class TlsHelper
return self::$useOpensslParse = true;
}
if ('\\' === DIRECTORY_SEPARATOR) {
if (Platform::isWindows()) {
// Windows is probably insecure in this case.
return self::$useOpensslParse = false;
}

@ -14,6 +14,7 @@ namespace Composer\Test;
use Composer\Console\Application;
use Composer\TestCase;
use Symfony\Component\Console\Output\OutputInterface;
class ApplicationTest extends TestCase
{
@ -30,11 +31,19 @@ class ApplicationTest extends TestCase
$index = 0;
if (extension_loaded('xdebug')) {
$outputMock->expects($this->at($index++))
->method("getVerbosity")
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->at($index++))
->method("write")
->with($this->equalTo('<warning>You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug</warning>'));
}
$outputMock->expects($this->at($index++))
->method("getVerbosity")
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->at($index++))
->method("write")
->with($this->equalTo(sprintf('<warning>Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF'])));

@ -113,7 +113,7 @@ class ClassMapGeneratorTest extends TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Could not scan for classes inside
* @expectedExceptionMessage does not exist
*/
public function testFindClassesThrowsWhenFileDoesNotExist()
{

@ -709,7 +709,7 @@ class SolverTest extends TestCase
$msg .= "Potential causes:\n";
$msg .= " - A typo in the package name\n";
$msg .= " - The package is not available in a stable-enough version according to your minimum-stability setting\n";
$msg .= " see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.\n\n";
$msg .= " see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n\n";
$msg .= "Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
$this->assertEquals($msg, $e->getMessage());
}

@ -16,6 +16,7 @@ use Composer\Downloader\GitDownloader;
use Composer\Config;
use Composer\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
class GitDownloaderTest extends TestCase
{
@ -353,7 +354,7 @@ class GitDownloaderTest extends TestCase
private function winCompat($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$cmd = str_replace('cd ', 'cd /D ', $cmd);
$cmd = str_replace('composerPath', getcwd().'/composerPath', $cmd);

@ -15,6 +15,7 @@ namespace Composer\Test\Downloader;
use Composer\Downloader\HgDownloader;
use Composer\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
class HgDownloaderTest extends TestCase
{
@ -156,10 +157,6 @@ class HgDownloaderTest extends TestCase
private function getCmd($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return strtr($cmd, "'", '"');
}
return $cmd;
return Platform::isWindows() ? strtr($cmd, "'", '"') : $cmd;
}
}

@ -15,6 +15,7 @@ namespace Composer\Test\Downloader;
use Composer\Downloader\XzDownloader;
use Composer\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
use Composer\Util\RemoteFilesystem;
class XzDownloaderTest extends TestCase
@ -31,7 +32,7 @@ class XzDownloaderTest extends TestCase
public function setUp()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
$this->markTestSkipped('Skip test on Windows');
}
$this->testDir = $this->getUniqueTmpDirectory();

@ -15,9 +15,11 @@ namespace Composer\Test\EventDispatcher;
use Composer\EventDispatcher\Event;
use Composer\Installer\InstallerEvents;
use Composer\TestCase;
use Composer\IO\BufferIO;
use Composer\Script\ScriptEvents;
use Composer\Script\CommandEvent;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Output\OutputInterface;
class EventDispatcherTest extends TestCase
{
@ -101,7 +103,7 @@ class EventDispatcherTest extends TestCase
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->setConstructorArgs(array(
$this->getMock('Composer\Composer'),
$io = $this->getMock('Composer\IO\IOInterface'),
$io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
$process,
))
->setMethods(array(
@ -123,23 +125,12 @@ class EventDispatcherTest extends TestCase
->method('getListeners')
->will($this->returnValue($listeners));
$io->expects($this->any())
->method('isVerbose')
->willReturn(1);
$io->expects($this->at(1))
->method('writeError')
->with($this->equalTo('> post-install-cmd: echo -n foo'));
$io->expects($this->at(3))
->method('writeError')
->with($this->equalTo('> post-install-cmd: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'));
$io->expects($this->at(5))
->method('writeError')
->with($this->equalTo('> post-install-cmd: echo -n bar'));
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
$expected = '> post-install-cmd: echo -n foo'.PHP_EOL.
'> post-install-cmd: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL.
'> post-install-cmd: echo -n bar'.PHP_EOL;
$this->assertEquals($expected, $io->getOutput());
}
public function testDispatcherCanExecuteComposerScriptGroups()
@ -148,7 +139,7 @@ class EventDispatcherTest extends TestCase
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->setConstructorArgs(array(
$composer = $this->getMock('Composer\Composer'),
$io = $this->getMock('Composer\IO\IOInterface'),
$io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
$process,
))
->setMethods(array(
@ -174,31 +165,13 @@ class EventDispatcherTest extends TestCase
return array();
}));
$io->expects($this->any())
->method('isVerbose')
->willReturn(1);
$io->expects($this->at(1))
->method('writeError')
->with($this->equalTo('> root: @group'));
$io->expects($this->at(3))
->method('writeError')
->with($this->equalTo('> group: echo -n foo'));
$io->expects($this->at(5))
->method('writeError')
->with($this->equalTo('> group: @subgroup'));
$io->expects($this->at(7))
->method('writeError')
->with($this->equalTo('> subgroup: echo -n baz'));
$io->expects($this->at(9))
->method('writeError')
->with($this->equalTo('> group: echo -n bar'));
$dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
$expected = '> root: @group'.PHP_EOL.
'> group: echo -n foo'.PHP_EOL.
'> group: @subgroup'.PHP_EOL.
'> subgroup: echo -n baz'.PHP_EOL.
'> group: echo -n bar'.PHP_EOL;
$this->assertEquals($expected, $io->getOutput());
}
/**

@ -24,12 +24,12 @@ Abandoned packages are flagged
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
Loading composer repositories with package information
Installing dependencies (including require-dev)
<warning>Package a/a is abandoned, you should avoid using it. No replacement was suggested.</warning>
<warning>Package c/c is abandoned, you should avoid using it. Use b/b instead.</warning>
<info>Writing lock file</info>
<info>Generating autoload files</info>
Writing lock file
Generating autoload files
--EXPECT--
Installing a/a (1.0.0)

@ -21,9 +21,9 @@ Broken dependencies should not lead to a replacer being installed which is not m
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
<error>Your requirements could not be resolved to an installable set of packages.</error>
Loading composer repositories with package information
Installing dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- c/c 1.0.0 requires x/x 1.0 -> no matching package found.
@ -33,7 +33,7 @@ install
Potential causes:
- A typo in the package name
- The package is not available in a stable-enough version according to your minimum-stability setting
see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.
see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.
Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

@ -0,0 +1,45 @@
--TEST--
See Github issue #4319 ( github.com/composer/composer/issues/4319 ).
Present a clear error message when config.platform.php version results in a conflict rule.
--CONDITION--
!defined('HHVM_VERSION')
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "a", "version": "1.0.0", "require": { "php": "5.5" } }
]
}
],
"require": {
"a": "~1.0"
},
"config": {
"platform": {
"php": "5.3"
}
}
}
--RUN--
install
--EXPECT-OUTPUT--
Loading composer repositories with package information
Installing dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Installation request for a ~1.0 -> satisfiable by a[1.0.0].
- a 1.0.0 requires php 5.5 -> your PHP version (%s) overriden by "config.platform.php" version (5.3) does not satisfy that requirement.
--EXPECT--
--EXPECT-EXIT-CODE--
2

@ -0,0 +1,47 @@
--TEST--
See Github issue #4795 ( github.com/composer/composer/issues/4795 ).
Composer\Installer::whitelistUpdateDependencies intentionally ignores root requirements even if said package is also a
dependency of one the requirements that is whitelisted for update.
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "a", "version": "1.0.0" },
{ "name": "a", "version": "1.1.0" },
{ "name": "b", "version": "1.0.0", "require": { "a": "~1.0" } },
{ "name": "b", "version": "1.1.0", "require": { "a": "~1.1" } },
{ "name": "c", "version": "1.0.0", "require": { "a": "~1.0" } }
]
}
],
"require": {
"a": "~1.0",
"b": "~1.0",
"c": "~1.0"
}
}
--INSTALLED--
[
{ "name": "a", "version": "1.0.0" },
{ "name": "b", "version": "1.0.0", "require": { "a": "~1.0" } },
{ "name": "c", "version": "1.0.0", "require": { "a": "~1.0" } }
]
--RUN--
update B --with-dependencies
--EXPECT-OUTPUT--
<warning>Dependency "a" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Writing lock file
Generating autoload files
--EXPECT--

@ -0,0 +1,16 @@
--TEST--
Tries to require a package with the same name as the root package
--COMPOSER--
{
"name": "foo/bar",
"require": {
"foo/bar": "@dev"
}
}
--RUN--
install
--EXPECT-EXCEPTION--
InvalidArgumentException
--EXPECT--
Root package 'foo/bar' cannot require itself in its composer.json
Did you accidentally name your root package after an external package?

@ -19,10 +19,10 @@ Suggestions are not displayed for installed packages
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
<info>Writing lock file</info>
<info>Generating autoload files</info>
Loading composer repositories with package information
Installing dependencies (including require-dev)
Writing lock file
Generating autoload files
--EXPECT--
Installing a/a (1.0.0)

@ -17,10 +17,10 @@ Suggestions are not displayed in non-dev mode
--RUN--
install --no-dev
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies</info>
<info>Writing lock file</info>
<info>Generating autoload files</info>
Loading composer repositories with package information
Installing dependencies
Writing lock file
Generating autoload files
--EXPECT--
Installing a/a (1.0.0)

@ -19,10 +19,10 @@ Suggestions are not displayed for packages if they are replaced
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
<info>Writing lock file</info>
<info>Generating autoload files</info>
Loading composer repositories with package information
Installing dependencies (including require-dev)
Writing lock file
Generating autoload files
--EXPECT--
Installing c/c (1.0.0)

@ -17,11 +17,11 @@ Suggestions are displayed
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
Loading composer repositories with package information
Installing dependencies (including require-dev)
a/a suggests installing b/b (an obscure reason)
<info>Writing lock file</info>
<info>Generating autoload files</info>
Writing lock file
Generating autoload files
--EXPECT--
Installing a/a (1.0.0)

@ -14,6 +14,7 @@ namespace Composer\Test\IO;
use Composer\IO\ConsoleIO;
use Composer\TestCase;
use Symfony\Component\Console\Output\OutputInterface;
class ConsoleIOTest extends TestCase
{
@ -40,6 +41,9 @@ class ConsoleIOTest extends TestCase
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->once())
->method('getVerbosity')
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->once())
->method('write')
->with($this->equalTo('some information about something'), $this->equalTo(false));
@ -53,6 +57,9 @@ class ConsoleIOTest extends TestCase
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\ConsoleOutputInterface');
$outputMock->expects($this->once())
->method('getVerbosity')
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->once())
->method('getErrorOutput')
->willReturn($outputMock);
@ -69,6 +76,9 @@ class ConsoleIOTest extends TestCase
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->once())
->method('getVerbosity')
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->once())
->method('write')
->with(
@ -95,25 +105,28 @@ class ConsoleIOTest extends TestCase
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->at(0))
$outputMock->expects($this->any())
->method('getVerbosity')
->willReturn(OutputInterface::VERBOSITY_NORMAL);
$outputMock->expects($this->at(1))
->method('write')
->with($this->equalTo('something (<question>strlen = 23</question>)'));
$outputMock->expects($this->at(1))
$outputMock->expects($this->at(3))
->method('write')
->with($this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false));
$outputMock->expects($this->at(2))
$outputMock->expects($this->at(5))
->method('write')
->with($this->equalTo('shorter (<comment>12</comment>)'), $this->equalTo(false));
$outputMock->expects($this->at(3))
$outputMock->expects($this->at(7))
->method('write')
->with($this->equalTo(str_repeat(' ', 11)), $this->equalTo(false));
$outputMock->expects($this->at(4))
$outputMock->expects($this->at(9))
->method('write')
->with($this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false));
$outputMock->expects($this->at(5))
$outputMock->expects($this->at(11))
->method('write')
->with($this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false));
$outputMock->expects($this->at(6))
$outputMock->expects($this->at(13))
->method('write')
->with($this->equalTo('something longer than initial (<info>34</info>)'));

@ -26,7 +26,10 @@ use Composer\Test\Mock\InstalledFilesystemRepositoryMock;
use Composer\Test\Mock\InstallationManagerMock;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Composer\TestCase;
use Composer\IO\BufferIO;
class InstallerTest extends TestCase
{
@ -137,7 +140,7 @@ class InstallerTest extends TestCase
/**
* @dataProvider getIntegrationTests
*/
public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode)
public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult)
{
if ($condition) {
eval('$res = '.$condition.';');
@ -146,18 +149,15 @@ class InstallerTest extends TestCase
}
}
$output = null;
$io = $this->getMock('Composer\IO\IOInterface');
$callback = function ($text, $newline) use (&$output) {
$output .= $text . ($newline ? "\n" : "");
};
$io->expects($this->any())
->method('write')
->will($this->returnCallback($callback));
$io->expects($this->any())
->method('writeError')
->will($this->returnCallback($callback));
$io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false));
// Prepare for exceptions
if (!is_int($expectResult)) {
$normalizedOutput = rtrim(str_replace("\n", PHP_EOL, $expect));
$this->setExpectedException($expectResult, $normalizedOutput);
}
// Create Composer mock object according to configuration
$composer = FactoryMock::create($io, $composerConfig);
$jsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
@ -233,8 +233,14 @@ class InstallerTest extends TestCase
$appOutput = fopen('php://memory', 'w+');
$result = $application->run(new StringInput($run), new StreamOutput($appOutput));
fseek($appOutput, 0);
$this->assertEquals($expectExitCode, $result, $output . stream_get_contents($appOutput));
// Shouldn't check output and results if an exception was expected by this point
if (!is_int($expectResult)) {
return;
}
$output = str_replace("\r", '', $io->getOutput());
$this->assertEquals($expectResult, $result, $output . stream_get_contents($appOutput));
if ($expectLock) {
unset($actualLock['hash']);
unset($actualLock['content-hash']);
@ -246,7 +252,7 @@ class InstallerTest extends TestCase
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
if ($expectOutput) {
$this->assertEquals(rtrim($expectOutput), rtrim($output));
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
}
}
@ -266,7 +272,7 @@ class InstallerTest extends TestCase
$installedDev = array();
$lock = array();
$expectLock = array();
$expectExitCode = 0;
$expectResult = 0;
try {
$message = $testData['TEST'];
@ -303,12 +309,21 @@ class InstallerTest extends TestCase
}
$expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null;
$expect = $testData['EXPECT'];
$expectExitCode = isset($testData['EXPECT-EXIT-CODE']) ? (int) $testData['EXPECT-EXIT-CODE'] : 0;
if (!empty($testData['EXPECT-EXCEPTION'])) {
$expectResult = $testData['EXPECT-EXCEPTION'];
if (!empty($testData['EXPECT-EXIT-CODE'])) {
throw new \LogicException('EXPECT-EXCEPTION and EXPECT-EXIT-CODE are mutually exclusive');
}
} elseif (!empty($testData['EXPECT-EXIT-CODE'])) {
$expectResult = (int) $testData['EXPECT-EXIT-CODE'];
} else {
$expectResult = 0;
}
} catch (\Exception $e) {
die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file)));
}
$tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode);
$tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult);
}
return $tests;
@ -328,6 +343,7 @@ class InstallerTest extends TestCase
'EXPECT-LOCK' => false,
'EXPECT-OUTPUT' => false,
'EXPECT-EXIT-CODE' => false,
'EXPECT-EXCEPTION' => false,
'EXPECT' => true,
);

@ -0,0 +1,64 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Package\Archiver;
use Composer\Package\Archiver\ZipArchiver;
class ZipArchiverTest extends ArchiverTest
{
public function testZipArchive()
{
// Set up repository
$this->setupDummyRepo();
$package = $this->setupPackage();
$target = sys_get_temp_dir().'/composer_archiver_test.zip';
// Test archive
$archiver = new ZipArchiver();
$archiver->archive($package->getSourceUrl(), $target, 'zip');
$this->assertFileExists($target);
unlink($target);
}
/**
* Create a local dummy repository to run tests against!
*/
protected function setupDummyRepo()
{
$currentWorkDir = getcwd();
chdir($this->testDir);
$this->writeFile('file.txt', 'content', $currentWorkDir);
$this->writeFile('foo/bar/baz', 'content', $currentWorkDir);
$this->writeFile('foo/bar/ignoreme', 'content', $currentWorkDir);
$this->writeFile('x/baz', 'content', $currentWorkDir);
$this->writeFile('x/includeme', 'content', $currentWorkDir);
chdir($currentWorkDir);
}
protected function writeFile($path, $content, $currentWorkDir)
{
if (!file_exists(dirname($path))) {
mkdir(dirname($path), 0777, true);
}
$result = file_put_contents($path, 'a');
if (false === $result) {
chdir($currentWorkDir);
throw new \RuntimeException('Could not save file.');
}
}
}

@ -16,6 +16,7 @@ use Composer\Repository\Vcs\SvnDriver;
use Composer\Config;
use Composer\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
class SvnDriverTest extends TestCase
{
@ -71,7 +72,7 @@ class SvnDriverTest extends TestCase
private function getCmd($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (Platform::isWindows()) {
return strtr($cmd, "'", '"');
}

@ -0,0 +1,29 @@
<?php
/**
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Util;
use Composer\Util\Platform;
/**
* PlatformTest
*
* @author Niels Keurentjes <niels.keurentjes@omines.com>
*/
class PlatformTest extends \PHPUnit_Framework_TestCase
{
public function testWindows()
{
// Compare 2 common tests for Windows to the built-in Windows test
$this->assertEquals(('\\' === DIRECTORY_SEPARATOR), Platform::isWindows());
$this->assertEquals(defined('PHP_WINDOWS_VERSION_MAJOR'), Platform::isWindows());
}
}

@ -14,6 +14,7 @@ namespace Composer\Test\Util;
use Composer\Config;
use Composer\IO\NullIO;
use Composer\Util\Platform;
use Composer\Util\Svn;
class SvnTest extends \PHPUnit_Framework_TestCase
@ -131,10 +132,6 @@ class SvnTest extends \PHPUnit_Framework_TestCase
private function getCmd($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return strtr($cmd, "'", '"');
}
return $cmd;
return Platform::isWindows() ? strtr($cmd, "'", '"') : $cmd;
}
}

Loading…
Cancel
Save