diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index ffcb1589e..4981542e8 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -9,13 +9,13 @@ a logging library. If you have not yet installed Composer, refer to the > **Note:** for the sake of simplicity, this introduction will assume you > have performed a [local](00-intro.md#locally) install of Composer. -## `composer.json`: Project Setup +## `composer.json`: Project setup To start using Composer in your project, all you need is a `composer.json` file. This file describes the dependencies of your project and may contain other metadata as well. -### The `require` Key +### The `require` key The first (and often only) thing you specify in `composer.json` is the [`require`](04-schema.md#require) key. You are simply telling Composer which @@ -41,7 +41,7 @@ assumed that the `monolog/monolog` package is registered on Packagist. (See more about Packagist [below](#packagist), or read more about repositories [here](05-repositories.md)). -### Package Names +### Package names The package name consists of a vendor name and the project's name. Often these will be identical - the vendor name only exists to prevent naming clashes. For @@ -53,7 +53,7 @@ Read more about publishing packages and package naming [here](02-libraries.md). you to require certain versions of server software. See [platform packages](#platform-packages) below.) -### Package Version Constraints +### Package version constraints In our example, we are requesting the Monolog package with the version constraint [`1.0.*`](https://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*). @@ -84,7 +84,7 @@ versions, how versions relate to each other, and on version constraints. > versions of a package. Read more about stability flags and the `minimum-stability` > key on the [schema page](04-schema.md). -## Installing Dependencies +## Installing dependencies To install the defined dependencies for your project, run the [`install`](03-cli.md#install) command. @@ -95,7 +95,7 @@ php composer.phar install When you run this command, one of two things may happen: -### Installing Without `composer.lock` +### Installing without `composer.lock` If you have never run the command before and there is also no `composer.lock` file present, Composer simply resolves all dependencies listed in your `composer.json` file and downloads @@ -114,7 +114,7 @@ of them that it downloaded to the `composer.lock` file, locking the project to t versions. You should commit the `composer.lock` file to your project repo so that all people working on the project are locked to the same versions of dependencies (more below). -### Installing With `composer.lock` +### Installing with `composer.lock` This brings us to the second scenario. If there is already a `composer.lock` file as well as a `composer.json` file when you run `composer install`, it means either you ran the @@ -130,7 +130,7 @@ working on your project. As a result you will have all dependencies requested by the file was created). This is by design, it ensures that your project does not break because of unexpected changes in dependencies. -### Commit Your `composer.lock` File to Version Control +### Commit your `composer.lock` file to version control Committing this file to VC is important because it will cause anyone who sets up the project to use the exact same @@ -142,7 +142,7 @@ reinstalling the project you can feel confident the dependencies installed are still working even if your dependencies released many new versions since then. (See note below about using the `update` command.) -## Updating Dependencies to their Latest Versions +## Updating dependencies to their latest versions As mentioned above, the `composer.lock` file prevents you from automatically getting the latest versions of your dependencies. To update to the latest versions, use the diff --git a/doc/03-cli.md b/doc/03-cli.md index 8c1dfee70..54cf7642f 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -140,7 +140,7 @@ php composer.phar update vendor/package vendor/package2 You can also use wildcards to update a bunch of packages at once: ```sh -php composer.phar update vendor/* +php composer.phar update "vendor/*" ``` ### Options diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index 98a6d1335..d36eb11ff 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -89,6 +89,12 @@ Add this to your project's root `composer.json`: } ``` +Or let composer add it for you with: + +``` +php composer.phar require monolog/monolog:"dev-bugfix as 1.0.x-dev" +``` + That will fetch the `dev-bugfix` version of `monolog/monolog` from your GitHub and alias it to `1.0.x-dev`. diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 49cb138d8..89ba495cf 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -21,6 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Composer\Config; use Composer\Config\JsonConfigSource; use Composer\Factory; +use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Semver\VersionParser; use Composer\Package\BasePackage; @@ -284,7 +285,7 @@ EOT $value = json_encode($value); } - $this->getIO()->write($value); + $this->getIO()->write($value, true, IOInterface::QUIET); return 0; } @@ -710,9 +711,9 @@ EOT } if (is_string($rawVal) && $rawVal != $value) { - $io->write('[' . $k . $key . '] ' . $rawVal . ' (' . $value . ')'); + $io->write('[' . $k . $key . '] ' . $rawVal . ' (' . $value . ')', true, IOInterface::QUIET); } else { - $io->write('[' . $k . $key . '] ' . $value . ''); + $io->write('[' . $k . $key . '] ' . $value . '', true, IOInterface::QUIET); } } } diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index f15308bad..a28007a6b 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -194,7 +194,7 @@ EOT $status = $install->run(); if ($status !== 0) { - $this->revertComposerFile(); + $this->revertComposerFile(false); } return $status; @@ -225,7 +225,7 @@ EOT return; } - public function revertComposerFile() + public function revertComposerFile($hardExit = true) { $io = $this->getIO(); @@ -237,6 +237,8 @@ EOT file_put_contents($this->json->getPath(), $this->composerBackup); } - exit(1); + if ($hardExit) { + exit(1); + } } } diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 7cd3a9813..271c7261f 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -106,7 +106,7 @@ class Problem $msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but '; - if (defined('HHVM_VERSION')) { + if (defined('HHVM_VERSION') || count($available)) { return $msg . 'your HHVM version does not satisfy that requirement.'; } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 22e5cec25..4533a6b61 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -175,13 +175,18 @@ abstract class Rule return $text . ' -> your HHVM version does not satisfy that requirement.'; } - if ($targetName === 'hhvm') { - return $text . ' -> you are running this with PHP and not HHVM.'; - } - $packages = $pool->whatProvides($targetName); $package = count($packages) ? current($packages) : phpversion(); + if ($targetName === 'hhvm') { + if ($package instanceof CompletePackage) { + return $text . ' -> your HHVM version ('.$package->getPrettyVersion().') does not satisfy that requirement.'; + } else { + return $text . ' -> you are running this with PHP and not HHVM.'; + } + } + + if (!($package instanceof CompletePackage)) { return $text . ' -> your PHP version ('.phpversion().') does not satisfy that requirement.'; } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 66a43c37b..2188d99ce 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -58,6 +58,9 @@ class Solver /** @var array */ protected $learnedWhy = array(); + /** @var bool */ + public $testFlagLearnedPositiveLiteral = false; + /** @var IOInterface */ protected $io; @@ -478,6 +481,9 @@ class Solver unset($seen[abs($literal)]); if ($num && 0 === --$num) { + if ($literal < 0) { + $this->testFlagLearnedPositiveLiteral = true; + } $learnedLiterals[0] = -$literal; if (!$l1num) { diff --git a/src/Composer/Installer/BinaryInstaller.php b/src/Composer/Installer/BinaryInstaller.php index a14755bf1..0a7b97149 100644 --- a/src/Composer/Installer/BinaryInstaller.php +++ b/src/Composer/Installer/BinaryInstaller.php @@ -196,9 +196,13 @@ class BinaryInstaller dir=\$(cd "\${0%[/\\\\]*}" > /dev/null; cd $binDir && pwd) -if [ -d /proc/cygdrive ] && [[ \$(which php) == \$(readlink -n /proc/cygdrive)/* ]]; then - # We are in Cygwin using Windows php, so the path must be translated - dir=\$(cygpath -m "\$dir"); +if [ -d /proc/cygdrive ]; then + case \$(which php) in + \$(readlink -n /proc/cygdrive)/*) + # We are in Cygwin using Windows php, so the path must be translated + dir=\$(cygpath -m "\$dir"); + ;; + esac fi "\${dir}/$binFile" "\$@" diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 6593143d5..b1e20dbf5 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -48,7 +48,9 @@ class ArrayDumper if ($package->getSourceType()) { $data['source']['type'] = $package->getSourceType(); $data['source']['url'] = $package->getSourceUrl(); - $data['source']['reference'] = $package->getSourceReference(); + if (null !== ($value = $package->getSourceReference())) { + $data['source']['reference'] = $value; + } if ($mirrors = $package->getSourceMirrors()) { $data['source']['mirrors'] = $mirrors; } @@ -57,8 +59,12 @@ class ArrayDumper if ($package->getDistType()) { $data['dist']['type'] = $package->getDistType(); $data['dist']['url'] = $package->getDistUrl(); - $data['dist']['reference'] = $package->getDistReference(); - $data['dist']['shasum'] = $package->getDistSha1Checksum(); + if (null !== ($value = $package->getDistReference())) { + $data['dist']['reference'] = $value; + } + if (null !== ($value = $package->getDistSha1Checksum())) { + $data['dist']['shasum'] = $value; + } if ($mirrors = $package->getDistMirrors()) { $data['dist']['mirrors'] = $mirrors; } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 9fed111e0..c5edd0d68 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -152,7 +152,7 @@ class ArrayLoader implements LoaderInterface } $package->setSourceType($config['source']['type']); $package->setSourceUrl($config['source']['url']); - $package->setSourceReference($config['source']['reference']); + $package->setSourceReference(isset($config['source']['reference']) ? $config['source']['reference'] : null); if (isset($config['source']['mirrors'])) { $package->setSourceMirrors($config['source']['mirrors']); } diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 7d5e047dd..3c21139e8 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -16,15 +16,18 @@ use Composer\Package\CompletePackage; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginInterface; +use Composer\Util\ProcessExecutor; use Composer\Util\Silencer; +use Composer\Util\Platform; use Composer\XdebugHandler\XdebugHandler; +use Symfony\Component\Process\ExecutableFinder; /** * @author Jordi Boggiano */ class PlatformRepository extends ArrayRepository { - const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*)$}iD'; + const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-plugin-api)$}iD'; private $versionParser; @@ -37,8 +40,11 @@ class PlatformRepository extends ArrayRepository */ private $overrides = array(); - public function __construct(array $packages = array(), array $overrides = array()) + private $process; + + public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null) { + $this->process = $process === null ? (new ProcessExecutor()) : $process; foreach ($overrides as $name => $version) { $this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version); } @@ -220,12 +226,27 @@ class PlatformRepository extends ArrayRepository $this->addPackage($lib); } - if (defined('HHVM_VERSION')) { + $hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null; + if ($hhvmVersion === null && !Platform::isWindows()) { + $finder = new ExecutableFinder(); + $hhvm = $finder->find('hhvm'); + if ($hhvm !== null) { + $exitCode = $this->process->execute( + ProcessExecutor::escape($hhvm). + ' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null', + $hhvmVersion + ); + if ($exitCode !== 0) { + $hhvmVersion = null; + } + } + } + if ($hhvmVersion) { try { - $prettyVersion = HHVM_VERSION; + $prettyVersion = $hhvmVersion; $version = $this->versionParser->normalize($prettyVersion); } catch (\UnexpectedValueException $e) { - $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', HHVM_VERSION); + $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', $hhvmVersion); $version = $this->versionParser->normalize($prettyVersion); } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index ccb1810de..b80e7c61d 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -899,6 +899,9 @@ class SolverTest extends TestCase $this->request->install('A'); + // check correct setup for assertion later + $this->assertFalse($this->solver->testFlagLearnedPositiveLiteral); + $this->checkSolverResult(array( array('job' => 'install', 'package' => $packageF1), array('job' => 'install', 'package' => $packageD), @@ -908,6 +911,10 @@ class SolverTest extends TestCase array('job' => 'install', 'package' => $packageB), array('job' => 'install', 'package' => $packageA), )); + + // verify that the code path leading to a negative literal resulting in a positive learned literal is actually + // executed + $this->assertTrue($this->solver->testFlagLearnedPositiveLiteral); } protected function reposComplete() diff --git a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test index ccc9ead1a..5846d13c0 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -35,8 +35,7 @@ install --prefer-dist "dist": { "type": "zip", "url": "http://www.example.com/dist.zip", - "reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6", - "shasum": null + "reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6" }, "type": "library" } diff --git a/tests/Composer/Test/Fixtures/installer/update-changes-url.test b/tests/Composer/Test/Fixtures/installer/update-changes-url.test index 70294c8e6..d774ea188 100644 --- a/tests/Composer/Test/Fixtures/installer/update-changes-url.test +++ b/tests/Composer/Test/Fixtures/installer/update-changes-url.test @@ -101,43 +101,43 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an { "name": "a/a", "version": "dev-master", "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" }, - "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip", "shasum": null }, + "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" }, "type": "library" }, { "name": "b/b", "version": "2.0.3", "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" }, - "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip", "shasum": null }, + "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" }, "type": "library" }, { "name": "c/c", "version": "1.0.0", "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/newc", "type": "git" }, - "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": null }, + "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "type": "library" }, { "name": "d/d", "version": "dev-master", "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/newd", "type": "git" }, - "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": null }, + "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "type": "library" }, { "name": "e/e", "version": "dev-master", "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/e/newe", "type": "git" }, - "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": null }, + "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "type": "library" }, { "name": "f/f", "version": "dev-master", "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" }, - "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": null }, + "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "type": "library" }, { "name": "g/g", "version": "dev-master", "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/g/newg", "type": "git" }, - "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": null }, + "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "type": "library" } ], diff --git a/tests/Composer/Test/Repository/PlatformRepositoryTest.php b/tests/Composer/Test/Repository/PlatformRepositoryTest.php new file mode 100644 index 000000000..aa51a2fc6 --- /dev/null +++ b/tests/Composer/Test/Repository/PlatformRepositoryTest.php @@ -0,0 +1,70 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Repository; + +use Composer\Repository\PlatformRepository; +use Composer\Test\TestCase; +use Composer\Util\Platform; +use Symfony\Component\Process\ExecutableFinder; + +class PlatformRepositoryTest extends TestCase { + public function testHHVMVersionWhenExecutingInHHVM() { + if (!defined('HHVM_VERSION_ID')) { + $this->markTestSkipped('Not running with HHVM'); + return; + } + $repository = new PlatformRepository(); + $package = $repository->findPackage('hhvm', '*'); + $this->assertNotNull($package, 'failed to find HHVM package'); + $this->assertSame( + sprintf('%d.%d.%d', + HHVM_VERSION_ID / 10000, + (HHVM_VERSION_ID / 100) % 100, + HHVM_VERSION_ID % 100 + ), + $package->getPrettyVersion() + ); + } + + public function testHHVMVersionWhenExecutingInPHP() { + if (defined('HHVM_VERSION_ID')) { + $this->markTestSkipped('Running with HHVM'); + return; + } + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Test only works on PHP 5.4+'); + return; + } + if (Platform::isWindows()) { + $this->markTestSkipped('Test does not run on Windows'); + return; + } + $finder = new ExecutableFinder(); + $hhvm = $finder->find('hhvm'); + if ($hhvm === null) { + $this->markTestSkipped('HHVM is not installed'); + } + $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock(); + $process->expects($this->once())->method('execute')->will($this->returnCallback( + function($command, &$out) { + $this->assertContains('HHVM_VERSION', $command); + $out = '4.0.1-dev'; + return 0; + } + )); + $repository = new PlatformRepository(array(), array(), $process); + $package = $repository->findPackage('hhvm', '*'); + $this->assertNotNull($package, 'failed to find HHVM package'); + $this->assertSame('4.0.1.0-dev', $package->getVersion()); + } +}