diff --git a/composer.json b/composer.json
index 8fcd1ea93..f45775853 100644
--- a/composer.json
+++ b/composer.json
@@ -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": {
diff --git a/composer.lock b/composer.lock
index caeba6397..31543e65c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -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": [
{
diff --git a/doc/00-intro.md b/doc/00-intro.md
index 1d09f2339..872bdff2c 100644
--- a/doc/00-intro.md
+++ b/doc/00-intro.md
@@ -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
diff --git a/doc/03-cli.md b/doc/03-cli.md
index 94a7a963c..b35d8f805 100644
--- a/doc/03-cli.md
+++ b/doc/03-cli.md
@@ -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
diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php
index 94fccc2e5..722bc94cc 100644
--- a/src/Composer/Command/ConfigCommand.php
+++ b/src/Composer/Command/ConfigCommand.php
@@ -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;
}
diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php
index fff1f86eb..150b8c71b 100644
--- a/src/Composer/Command/HomeCommand.php
+++ b/src/Composer/Command/HomeCommand.php
@@ -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 . '"');
}
diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php
index fff159158..16b98020a 100644
--- a/src/Composer/Command/SelfUpdateCommand.php
+++ b/src/Composer/Command/SelfUpdateCommand.php
@@ -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)) {
diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php
index e3e1ffd71..5ddce2954 100644
--- a/src/Composer/Command/ShowCommand.php
+++ b/src/Composer/Command/ShowCommand.php
@@ -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--;
}
diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php
index f77876100..07d517eae 100644
--- a/src/Composer/Console/Application.php
+++ b/src/Composer/Console/Application.php
@@ -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;
@@ -219,7 +220,7 @@ class Application extends BaseApplication
}
Silencer::restore();
- if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
+ if (Platform::isWindows() && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
$io->writeError('The following exception may be caused by a stale entry in your cmd.exe AutoRun', true, IOInterface::QUIET);
$io->writeError('Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details', true, IOInterface::QUIET);
}
diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php
index 7db561889..48e0d4b39 100644
--- a/src/Composer/Downloader/GitDownloader.php
+++ b/src/Composer/Downloader/GitDownloader.php
@@ -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();
diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php
index ae7d7f17a..9969578e5 100644
--- a/src/Composer/Downloader/GzipDownloader.php
+++ b/src/Composer/Downloader/GzipDownloader.php
@@ -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);
+ }
}
diff --git a/src/Composer/Downloader/RarDownloader.php b/src/Composer/Downloader/RarDownloader.php
index 81e11785e..2a0c98cf9 100644
--- a/src/Composer/Downloader/RarDownloader.php
+++ b/src/Composer/Downloader/RarDownloader.php
@@ -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;
}
diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php
index 6faaaaa4f..5f483975c 100644
--- a/src/Composer/Downloader/ZipDownloader.php
+++ b/src/Composer/Downloader/ZipDownloader.php
@@ -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;
}
diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php
index 058709275..2f3491fd4 100644
--- a/src/Composer/Factory.php
+++ b/src/Composer/Factory.php
@@ -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, '\\', '/');
}
diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php
index 3172e596f..78ea92c03 100644
--- a/src/Composer/Installer.php
+++ b/src/Composer/Installer.php
@@ -1195,6 +1195,7 @@ class Installer
foreach ($requirePackages as $requirePackage) {
if (isset($skipPackages[$requirePackage->getName()])) {
+ $this->io->writeError('Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.');
continue;
}
$packageQueue->enqueue($requirePackage);
diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php
index b14659f7b..31090de00 100644
--- a/src/Composer/Installer/LibraryInstaller.php
+++ b/src/Composer/Installer/LibraryInstaller.php
@@ -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);
diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php
index 35f7855de..0e16fcb32 100644
--- a/src/Composer/Installer/PearInstaller.php
+++ b/src/Composer/Installer/PearInstaller.php
@@ -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) {
diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php
index 0fc7c49af..565ff39c4 100644
--- a/src/Composer/Package/Loader/RootPackageLoader.php
+++ b/src/Composer/Package/Loader/RootPackageLoader.php
@@ -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);
diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php
index c3266543b..7fa004eb5 100644
--- a/src/Composer/Repository/PathRepository.php
+++ b/src/Composer/Repository/PathRepository.php
@@ -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);
diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php
index 833c82c20..27f52aa65 100644
--- a/src/Composer/Repository/PlatformRepository.php
+++ b/src/Composer/Repository/PlatformRepository.php
@@ -114,6 +114,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 +147,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 +194,7 @@ class PlatformRepository extends ArrayRepository
}
$lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
- $lib->setDescription('The '.$name.' PHP library');
+ $lib->setDescription($description);
$this->addPackage($lib);
}
diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php
index c3b888163..8e7ac3d04 100644
--- a/src/Composer/Util/Filesystem.php
+++ b/src/Composer/Util/Filesystem.php
@@ -115,7 +115,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));
@@ -186,10 +186,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";
}
@@ -211,10 +211,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";
}
@@ -269,7 +269,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);
@@ -465,7 +465,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);
}
@@ -503,7 +503,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);
}
diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php
index c1eaeebe9..b57a64a1f 100644
--- a/src/Composer/Util/Perforce.php
+++ b/src/Composer/Util/Perforce.php
@@ -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)
diff --git a/src/Composer/Util/Platform.php b/src/Composer/Util/Platform.php
new file mode 100644
index 000000000..eafb88b7a
--- /dev/null
+++ b/src/Composer/Util/Platform.php
@@ -0,0 +1,28 @@
+
+ * Jordi Boggiano
+ *
+ * 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
+ */
+class Platform
+{
+ /**
+ * @return bool Whether the host machine is running a Windows OS
+ */
+ public static function isWindows()
+ {
+ return defined('PHP_WINDOWS_VERSION_BUILD');
+ }
+}
diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php
index f9499f09f..6b778e5eb 100644
--- a/src/Composer/Util/ProcessExecutor.php
+++ b/src/Composer/Util/ProcessExecutor.php
@@ -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());
}
diff --git a/src/Composer/Util/TlsHelper.php b/src/Composer/Util/TlsHelper.php
index 6ea5cf591..721e93825 100644
--- a/src/Composer/Util/TlsHelper.php
+++ b/src/Composer/Util/TlsHelper.php
@@ -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;
}
diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php
index 26437ada5..9c851fe9a 100644
--- a/tests/Composer/Test/Downloader/GitDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php
@@ -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);
diff --git a/tests/Composer/Test/Downloader/HgDownloaderTest.php b/tests/Composer/Test/Downloader/HgDownloaderTest.php
index 75dfa84aa..6b660e383 100644
--- a/tests/Composer/Test/Downloader/HgDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/HgDownloaderTest.php
@@ -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;
}
}
diff --git a/tests/Composer/Test/Downloader/XzDownloaderTest.php b/tests/Composer/Test/Downloader/XzDownloaderTest.php
index 418776d75..d8e77a2cb 100644
--- a/tests/Composer/Test/Downloader/XzDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/XzDownloaderTest.php
@@ -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();
diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-4795.test b/tests/Composer/Test/Fixtures/installer/github-issues-4795.test
new file mode 100644
index 000000000..6dc3ced3d
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/github-issues-4795.test
@@ -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--
+Dependency "a" is also a root requirement, but is not explicitly whitelisted. Ignoring.
+Loading composer repositories with package information
+Updating dependencies (including require-dev)
+Nothing to install or update
+Writing lock file
+Generating autoload files
+
+--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/install-self-from-root.test b/tests/Composer/Test/Fixtures/installer/install-self-from-root.test
new file mode 100644
index 000000000..82092c77f
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/install-self-from-root.test
@@ -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?
diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php
index b0a51fea7..eaf6caa03 100644
--- a/tests/Composer/Test/InstallerTest.php
+++ b/tests/Composer/Test/InstallerTest.php
@@ -140,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.';');
@@ -150,6 +150,14 @@ class InstallerTest extends TestCase
}
$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();
@@ -225,9 +233,14 @@ class InstallerTest extends TestCase
$appOutput = fopen('php://memory', 'w+');
$result = $application->run(new StringInput($run), new StreamOutput($appOutput));
fseek($appOutput, 0);
- $output = str_replace("\r", '', $io->getOutput());
- $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']);
@@ -259,7 +272,7 @@ class InstallerTest extends TestCase
$installedDev = array();
$lock = array();
$expectLock = array();
- $expectExitCode = 0;
+ $expectResult = 0;
try {
$message = $testData['TEST'];
@@ -296,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;
@@ -321,6 +343,7 @@ class InstallerTest extends TestCase
'EXPECT-LOCK' => false,
'EXPECT-OUTPUT' => false,
'EXPECT-EXIT-CODE' => false,
+ 'EXPECT-EXCEPTION' => false,
'EXPECT' => true,
);
diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
index c2ae497ca..881b86ea2 100644
--- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
@@ -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, "'", '"');
}
diff --git a/tests/Composer/Test/Util/PlatformTest.php b/tests/Composer/Test/Util/PlatformTest.php
new file mode 100644
index 000000000..3d82fb96f
--- /dev/null
+++ b/tests/Composer/Test/Util/PlatformTest.php
@@ -0,0 +1,29 @@
+
+ * Jordi Boggiano
+ *
+ * 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
+ */
+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());
+ }
+}
diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php
index 55a116376..c16b0e6ce 100644
--- a/tests/Composer/Test/Util/SvnTest.php
+++ b/tests/Composer/Test/Util/SvnTest.php
@@ -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;
}
}