From 74b4bcd22e68d0bdc446b37ad82d03d9929cb5cd Mon Sep 17 00:00:00 2001 From: Danack Date: Sun, 18 Aug 2013 22:37:18 +0100 Subject: [PATCH 001/638] Fix issue where none root composer.json could be used by ArtifactRepository http://www.php.net/manual/en/ziparchive.locatename.php#85512 --- .../Repository/ArtifactRepository.php | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 869e4757f..e554cac8b 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -74,6 +74,39 @@ class ArtifactRepository extends ArrayRepository } } + /** + * Find a file by name, returning the one that has the shortest path. + * + * @param \ZipArchive $zip + * @param $filename + * @return bool|int + */ + private function locateFile(\ZipArchive $zip, $filename) { + $shortestIndex = -1; + $shortestIndexLength = -1; + + for ($i = 0; $i < $zip->numFiles; $i++ ){ + $stat = $zip->statIndex($i); + if (strcmp(basename($stat['name']), $filename) === 0){ + $length = strlen($stat['name']); + if ($shortestIndex == -1 || $length < $shortestIndexLength) { + //Check it's not a directory. + $contents = $zip->getFromIndex($i); + if ($contents !== false) { + $shortestIndex = $i; + $shortestIndexLength = $length; + } + } + } + } + + if ($shortestIndex == -1) { + return false; + } + + return $shortestIndex; + } + private function getComposerInformation(\SplFileInfo $file) { $zip = new \ZipArchive(); @@ -83,7 +116,7 @@ class ArtifactRepository extends ArrayRepository return false; } - $foundFileIndex = $zip->locateName('composer.json', \ZipArchive::FL_NODIR); + $foundFileIndex = $this->locateFile($zip, 'composer.json'); if (false === $foundFileIndex) { return false; } From abfefd1faae02a72dfa1259440fdf6d7305af736 Mon Sep 17 00:00:00 2001 From: Danack Date: Sun, 18 Aug 2013 22:57:26 +0100 Subject: [PATCH 002/638] Improved variable name. --- src/Composer/Repository/ArtifactRepository.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index e554cac8b..769e35b40 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -82,29 +82,25 @@ class ArtifactRepository extends ArrayRepository * @return bool|int */ private function locateFile(\ZipArchive $zip, $filename) { - $shortestIndex = -1; - $shortestIndexLength = -1; + $indexOfShortestMatch = false; + $lengthOfShortestMatch = -1; for ($i = 0; $i < $zip->numFiles; $i++ ){ $stat = $zip->statIndex($i); if (strcmp(basename($stat['name']), $filename) === 0){ $length = strlen($stat['name']); - if ($shortestIndex == -1 || $length < $shortestIndexLength) { + if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) { //Check it's not a directory. $contents = $zip->getFromIndex($i); if ($contents !== false) { - $shortestIndex = $i; - $shortestIndexLength = $length; + $indexOfShortestMatch = $i; + $lengthOfShortestMatch = $length; } } } } - if ($shortestIndex == -1) { - return false; - } - - return $shortestIndex; + return $indexOfShortestMatch; } private function getComposerInformation(\SplFileInfo $file) From 2e2b66b16e16d3b67e478c33f601f1ad8c7808c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ot=C3=A1vio=20Cobucci=20Oblonczyk?= Date: Mon, 19 Aug 2013 04:36:57 -0300 Subject: [PATCH 003/638] Package should have download options --- src/Composer/Package/BasePackage.php | 18 ++++++++++++++++++ src/Composer/Package/PackageInterface.php | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index a783ca7fc..afc20f4b6 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -49,6 +49,7 @@ abstract class BasePackage implements PackageInterface protected $repository; protected $id; + protected $options; /** * All descendants' constructors should call this parent constructor @@ -60,6 +61,7 @@ abstract class BasePackage implements PackageInterface $this->prettyName = $name; $this->name = strtolower($name); $this->id = -1; + $this->options = array(); } /** @@ -133,6 +135,22 @@ abstract class BasePackage implements PackageInterface return $this->repository; } + /** + * {@inheritDoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + public function setOptions(array $options) + { + $this->options = $options; + } + /** * checks if this package is a platform package * diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index a3c8a2793..1b1e99707 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -301,4 +301,18 @@ interface PackageInterface * @return array */ public function getArchiveExcludes(); + + /** + * Configures the list of options to download package dist files + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Returns a list of options to download package dist files + * + * @return array + */ + public function getOptions(); } From d4043b6b9a01bc583a590902d827a4bd5aeb66d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ot=C3=A1vio=20Cobucci=20Oblonczyk?= Date: Mon, 19 Aug 2013 04:38:25 -0300 Subject: [PATCH 004/638] Package options must be passed as argument to downloader --- src/Composer/Downloader/FileDownloader.php | 4 ++-- tests/Composer/Test/Downloader/FileDownloaderTest.php | 8 ++++++++ tests/Composer/Test/Downloader/ZipDownloaderTest.php | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 8ed0712bf..38b2fe304 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -103,7 +103,7 @@ class FileDownloader implements DownloaderInterface $retries = 3; while ($retries--) { try { - $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); + $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions()); break; } catch (TransportException $e) { // if we got an http response with a proper code, then requesting again will probably not help, abort @@ -132,7 +132,7 @@ class FileDownloader implements DownloaderInterface ) { throw $e; } - $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); + $this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions()); } else { throw $e; } diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php index 99dcaab18..a6f3f1984 100644 --- a/tests/Composer/Test/Downloader/FileDownloaderTest.php +++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php @@ -89,6 +89,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getDistUrl') ->will($this->returnValue('http://example.com/script.js')) ; + $packageMock->expects($this->atLeastOnce()) + ->method('getOptions') + ->will($this->returnValue(array())) + ; do { $path = sys_get_temp_dir().'/'.md5(time().mt_rand()); @@ -130,6 +134,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getDistUrl') ->will($this->returnValue('http://example.com/script.js')) ; + $packageMock->expects($this->atLeastOnce()) + ->method('getOptions') + ->will($this->returnValue(array())) + ; $packageMock->expects($this->any()) ->method('getDistSha1Checksum') ->will($this->returnValue('invalid')) diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php index bbe77d7ee..441777d8c 100644 --- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php @@ -30,6 +30,10 @@ class ZipDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getDistUrl') ->will($this->returnValue('file://'.__FILE__)) ; + $packageMock->expects($this->atLeastOnce()) + ->method('getOptions') + ->will($this->returnValue(array())) + ; $io = $this->getMock('Composer\IO\IOInterface'); $config = $this->getMock('Composer\Config'); From 28d0e087854558f5c133a9efc030efa8c9e9bf4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ot=C3=A1vio=20Cobucci=20Oblonczyk?= Date: Mon, 19 Aug 2013 04:39:13 -0300 Subject: [PATCH 005/638] Package options should be dumped if exists --- src/Composer/Package/Dumper/ArrayDumper.php | 4 ++++ tests/Composer/Test/Package/Dumper/ArrayDumperTest.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index bca932d71..0fea4578d 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -108,6 +108,10 @@ class ArrayDumper } } + if (count($package->getOptions()) > 0) { + $data['options'] = $package->getOptions(); + } + return $data; } diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 4b9877523..54e8de9cc 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -194,6 +194,10 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase array(new Link('foo', 'foo/bar', new VersionConstraint('=', '1.0.0.0'), 'requires', '1.0.0'), new Link('bar', 'bar/baz', new VersionConstraint('=', '1.0.0.0'), 'requires', '1.0.0')), 'conflicts', array('bar/baz' => '1.0.0', 'foo/bar' => '1.0.0') + ), + array( + 'options', + array('ssl' => array('local_cert' => '/opt/certs/test.pem')) ) ); } From 8630aab93f03efd2c1e74a7e0498ed702c9a6598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ot=C3=A1vio=20Cobucci=20Oblonczyk?= Date: Mon, 19 Aug 2013 04:40:08 -0300 Subject: [PATCH 006/638] Package options must be a non mandatory array and should be loaded when exists --- src/Composer/Package/Loader/ArrayLoader.php | 4 ++++ src/Composer/Package/Loader/ValidatingArrayLoader.php | 1 + tests/Composer/Test/Package/Loader/ArrayLoaderTest.php | 1 + .../Test/Package/Loader/ValidatingArrayLoaderTest.php | 10 ++++++++++ 4 files changed, 16 insertions(+) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 3940bdeb0..46e33b37d 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -193,6 +193,10 @@ class ArrayLoader implements LoaderInterface } } + if (isset($config['options'])) { + $package->setOptions($config['options']); + } + return $package; } diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index a5b6281a3..8e21b0e91 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -196,6 +196,7 @@ class ValidatingArrayLoader implements LoaderInterface // TODO validate package repositories' packages using this recursively $this->validateFlatArray('include-path'); + $this->validateArray('options'); // branch alias validation if (isset($this->config['extra']['branch-alias'])) { diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 248e251ef..474929307 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -117,6 +117,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase 'archive' => array( 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), ), + 'options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) ); $package = $this->loader->load($config); diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 262c24bf6..b069c3e82 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -143,6 +143,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'bin/foo', 'bin/bar', ), + 'options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) ), ), array( // test as array @@ -256,6 +257,15 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'autoload : invalid value (psr0), must be one of psr-0, classmap, files' ) ), + array( + array( + 'name' => 'foo/bar', + 'options' => 'test', + ), + array( + 'options : should be an array, string given' + ) + ), ); } From 0b77a59af66c7611307139aec49014fbe2db3d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ot=C3=A1vio=20Cobucci=20Oblonczyk?= Date: Mon, 19 Aug 2013 04:40:54 -0300 Subject: [PATCH 007/638] Repository options must be replicated on package when dist file is under repository base dir --- .../Repository/ComposerRepository.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5eab777a4..2b6dd2385 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -13,6 +13,7 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Package; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; @@ -204,10 +205,19 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $package = $package->getAliasOf(); } $package->setRepository($this); + $this->configurePackageOptions($package); return $package; } + protected function configurePackageOptions(PackageInterface $package) + { + if ($package instanceof Package + && strpos($package->getDistUrl(), $this->baseUrl) === 0) { + $package->setOptions($this->options); + } + } + /** * {@inheritDoc} */ @@ -376,6 +386,17 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } } + /** + * Adds a new package to the repository + * + * @param PackageInterface $package + */ + public function addPackage(PackageInterface $package) + { + parent::addPackage($package); + $this->configurePackageOptions($package); + } + protected function loadRootServerFile() { if (null !== $this->rootData) { From 652715f4c2f066f2d68aeaabea707ec3cc42574f Mon Sep 17 00:00:00 2001 From: schmkr Date: Mon, 16 Sep 2013 18:10:24 +0200 Subject: [PATCH 008/638] Improved the handling of trunkPath - $this->baseUrl is only used if $this->trunkPath === false, otherwise we will use $this->baseUrl with $this->trunkPath. - scanning through trunkPath will now look for composer.json file instead of a path that matches $this->trunkPath, beacuse checking against the latter failed with deeper trunkPaths - $this->rootIdentifier is now 'trunk' no matter how deep $this->trunkPath is (with deeper trunkPaths, the name became something like "dev-trunk-devel-team-packages-package" --- src/Composer/Repository/Vcs/SvnDriver.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index c5a67b455..2ac05e712 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -193,10 +193,10 @@ class SvnDriver extends VcsDriver if (null === $this->branches) { $this->branches = array(); - if (false === strpos($this->trunkPath, '/')) { + if(false === $this->trunkPath) { $trunkParent = $this->baseUrl . '/'; } else { - $trunkParent = $this->baseUrl . '/' . dirname($this->trunkPath) . '/'; + $trunkParent = $this->baseUrl . '/' . $this->trunkPath; } $output = $this->execute('svn ls --verbose', $trunkParent); @@ -204,12 +204,12 @@ class SvnDriver extends VcsDriver foreach ($this->process->splitLines($output) as $line) { $line = trim($line); if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { - if (isset($match[1]) && isset($match[2]) && $match[2] === $this->trunkPath . '/') { - $this->branches[$this->trunkPath] = $this->buildIdentifier( + if (isset($match[1]) && isset($match[2]) && $match[2] === 'composer.json') { + $this->branches['trunk'] = $this->buildIdentifier( '/' . $this->trunkPath, $match[1] ); - $this->rootIdentifier = $this->branches[$this->trunkPath]; + $this->rootIdentifier = $this->branches['trunk']; break; } } From c4d7347ec551793e3171f25cce4a9e7957820da8 Mon Sep 17 00:00:00 2001 From: schmkr Date: Mon, 16 Sep 2013 22:13:37 +0200 Subject: [PATCH 009/638] Fixed a coding style issue Missing a space between if and ( --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2ac05e712..a21edf5f5 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -193,7 +193,7 @@ class SvnDriver extends VcsDriver if (null === $this->branches) { $this->branches = array(); - if(false === $this->trunkPath) { + if (false === $this->trunkPath) { $trunkParent = $this->baseUrl . '/'; } else { $trunkParent = $this->baseUrl . '/' . $this->trunkPath; From 6cc95c43e3cd528a39cc423ee47089dbb9a01b25 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 12 Oct 2013 02:29:16 +0200 Subject: [PATCH 010/638] Add a warning for unbound constraints in the validating loader Closes #2165 --- .../Package/Loader/ValidatingArrayLoader.php | 19 ++++++++++++++- .../Loader/ValidatingArrayLoaderTest.php | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index a5b6281a3..d7dcf293d 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -14,6 +14,7 @@ namespace Composer\Package\Loader; use Composer\Package; use Composer\Package\BasePackage; +use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Version\VersionParser; /** @@ -142,6 +143,8 @@ class ValidatingArrayLoader implements LoaderInterface } } + $unboundConstraint = new VersionConstraint('=', $this->versionParser->normalize('dev-master')); + foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) { if ($this->validateArray($linkType) && isset($this->config[$linkType])) { foreach ($this->config[$linkType] as $package => $constraint) { @@ -153,13 +156,27 @@ class ValidatingArrayLoader implements LoaderInterface unset($this->config[$linkType][$package]); } elseif ('self.version' !== $constraint) { try { - $this->versionParser->parseConstraints($constraint); + $linkConstraint = $this->versionParser->parseConstraints($constraint); } catch (\Exception $e) { $this->errors[] = $linkType.'.'.$package.' : invalid version constraint ('.$e->getMessage().')'; unset($this->config[$linkType][$package]); + continue; + } + + if ('conflict' === $linkType || 'require-dev' === $linkType) { + continue; // conflict can be unbound, and require-dev constraints will not impact shared libraries as they are root-only + } + + if ($linkConstraint->matches($unboundConstraint)) { + $this->warnings[] = $linkType.'.'.$package.' : unbound version constraint detected ('.$constraint.')'; + unset($this->config[$linkType][$package]); } } } + + if (empty($this->config[$linkType])) { + unset($this->config[$linkType]); + } } } diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 262c24bf6..6bc3b5e25 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -73,14 +73,17 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase ), 'require' => array( 'a/b' => '1.*', + 'b/c' => '~2', 'example' => '>2.0-dev,<2.4-dev', ), 'require-dev' => array( 'a/b' => '1.*', + 'b/c' => '*', 'example' => '>2.0-dev,<2.4-dev', ), 'conflict' => array( 'a/b' => '1.*', + 'b/c' => '>2.7', 'example' => '>2.0-dev,<2.4-dev', ), 'replace' => array( @@ -288,6 +291,27 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'support.wiki : invalid value (foo:bar), must be an http/https URL', ) ), + array( + array( + 'name' => 'foo/bar', + 'require' => array( + 'foo/baz' => '*', + 'bar/baz' => '>=1.0', + ), + 'provide' => array( + 'bar/foo' => 'dev-master', + ), + 'replace' => array( + 'bar/hacked' => '@stable', + ) + ), + array( + 'require.foo/baz : unbound version constraint detected (*)', + 'require.bar/baz : unbound version constraint detected (>=1.0)', + 'provide.bar/foo : unbound version constraint detected (dev-master)', + 'replace.bar/hacked : unbound version constraint detected (@stable)', + ) + ), ); } } From c7576ee64993ae29c59619ee815290f8a0c8a292 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 12 Oct 2013 02:57:14 +0200 Subject: [PATCH 011/638] Added a new FAQ entry about unbound version constraints --- ...-unbound-version-constraints-a-bad-idea.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md diff --git a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md new file mode 100644 index 000000000..a85bf35f6 --- /dev/null +++ b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md @@ -0,0 +1,29 @@ +# Why are unbound version constraints a bad idea? + +A version constraint without an upper bound will allow any future version of +the dependency, even newer major version breaking backward compatibility +(which is the only reason to bump the major version when following semver). + +Once a release of your package is tagged, you cannot tweak its dependencies +anymore in case a dependency breaks BC (you have to do a new release but the +previous one stays broken). + +These leaves you with 3 alternatives to avoid having broken releases: + +- defining an upper bound on your constraint (which you will increase in a + new release after testing that your package is compatible with the new + version) + +- knowing all future changes of your dependency to guarantee the compatibility + of the current code. Forget this alternative unless you are Chuck Norris :) + +- never release your package, but this means that all users will have to + whitelist the dev versions to install it (and complain about it) + +The recommended way is of course to define an upper bound on your constraint, +so Composer will show you a warning for unbound constraints when validating +your `composer.json` file. + +As a package maintainer, you can make the life of your users easier by +providing an [alias version](../articles/aliases.md) for your development +branch to allow it to match bound constraints. From 5267622bba3adecff89e554b4591a06db41a054d Mon Sep 17 00:00:00 2001 From: Pierre du Plessis Date: Wed, 4 Dec 2013 09:54:41 +0200 Subject: [PATCH 012/638] Add remove command to remove a package from the list of current installed packages --- doc/03-cli.md | 18 +++++ src/Composer/Command/RemoveCommand.php | 106 +++++++++++++++++++++++++ src/Composer/Console/Application.php | 1 + 3 files changed, 125 insertions(+) create mode 100755 src/Composer/Command/RemoveCommand.php diff --git a/doc/03-cli.md b/doc/03-cli.md index 9c07776ff..74e9958ad 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -150,6 +150,24 @@ to the command. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. +## remove + +The `remove` command removes packages from the `composer.json` file from +the current directory. + + $ php composer.phar remove + +After removing the requirements, the modified requirements will be +uninstalled. + +### Options +* **--dry-run:** If you want to run through an uninstallation without actually + uninstalling a package, you can use `--dry-run`. This will simulate the + uninstallation and show you what would happen. +* **--dev:** Add packages to `require-dev`. +* **--no-update:** Only remove the package from the composer.json file, but + won't remove the files or update the composer.lock + ## global The global command allows you to run other commands like `install`, `require` diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php new file mode 100755 index 000000000..97e6adc0e --- /dev/null +++ b/src/Composer/Command/RemoveCommand.php @@ -0,0 +1,106 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\Config\JsonConfigSource; +use Composer\Installer; +use Composer\Plugin\CommandEvent; +use Composer\Plugin\PluginEvents; +use Composer\Json\JsonFile; +use Composer\Factory; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Pierre du Plessis + */ +class RemoveCommand extends Command +{ + protected function configure() + { + $this + ->setName('remove') + ->setDescription('Removes a package from the require or require-dev') + ->setDefinition(array( + new InputArgument('packages', InputArgument::IS_ARRAY, 'Packages that should be removed, if not provided all packages are.'), + new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section'), + new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.') + )) + ->setHelp(<<remove command removes a package from the current +list of installed packages + +php composer.phar remove + +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $composer = $this->getComposer(); + $packages = $input->getArgument('packages'); + + $io = $this->getIO(); + + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + + $file = Factory::getComposerFile(); + + $json = new JsonFile($file); + $composerBackup = file_get_contents($json->getPath()); + + $json = new JsonConfigSource($json); + + $type = $input->getOption('dev') ? 'require-dev' : 'require'; + + foreach ($packages as $package) { + $json->removeLink($type, $package); + } + + if ($input->getOption('no-update')) { + if ($input->getOption('dry-run')) { + file_put_contents($json->getPath(), $composerBackup); + } + + return 0; + } + + $composer = Factory::create($io); + + $install = Installer::create($io, $composer); + + $install + ->setDryRun($input->getOption('dry-run')) + ->setVerbose($input->getOption('verbose')) + ->setDevMode($input->getOption('dev')) + ->setUpdate(true) + ->setUpdateWhitelist($packages) + ; + + if (!$install->run()) { + $output->writeln("\n".'Remove failed, reverting '.$file.' to its original content.'); + file_put_contents($json->getPath(), $composerBackup); + + return 1; + } + + return 0; + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 9d622cf67..6350e06d2 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -227,6 +227,7 @@ class Application extends BaseApplication $commands[] = new Command\RunScriptCommand(); $commands[] = new Command\LicensesCommand(); $commands[] = new Command\GlobalCommand(); + $commands[] = new Command\RemoveCommand(); if ('phar:' === substr(__FILE__, 0, 5)) { $commands[] = new Command\SelfUpdateCommand(); From 040bbaca51b2468224c37f4626161c7cedc57793 Mon Sep 17 00:00:00 2001 From: Max Gfeller Date: Thu, 19 Dec 2013 07:54:16 +0100 Subject: [PATCH 013/638] Don't throw an exception if the called script is not one of the event-scripts. This makes it possible for one to define their own scripts like "make-release" etc. --- src/Composer/Command/RunScriptCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index c4a3a3563..4317ba08c 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -56,8 +56,6 @@ EOT if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script)); } - - throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $script)); } $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); From f1c1ba27b4456adbf9c3ab38be9c7c5f5e4f1282 Mon Sep 17 00:00:00 2001 From: Max Gfeller Date: Thu, 19 Dec 2013 07:58:58 +0100 Subject: [PATCH 014/638] Throw an exception if no listeners have been found for given event. --- src/Composer/EventDispatcher/EventDispatcher.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 9c2aee91f..2f19673c4 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -118,6 +118,10 @@ class EventDispatcher { $listeners = $this->getListeners($event); + if(sizeof($listeners) === 0) { + throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $event->getName())); + } + foreach ($listeners as $callable) { if (!is_string($callable) && is_callable($callable)) { call_user_func($callable, $event); From 421b09dc447036e4f2ef03dc07734d6a20170944 Mon Sep 17 00:00:00 2001 From: Max Gfeller Date: Thu, 19 Dec 2013 08:46:36 +0100 Subject: [PATCH 015/638] Check if a given event has registered any listeners. If not the script is not defined in the composer.json file --- src/Composer/Command/RunScriptCommand.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 4317ba08c..ceb3204f3 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -58,6 +58,12 @@ EOT } } + $hasListeners = $this->getComposer()->getEventDispatcher()->hasEventListeners(new \Composer\Script\CommandEvent($script, $this->getComposer(), $this->getIO())); + + if(!$hasListeners) { + throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $script)); + } + $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); } } From a12ed492ef52c72575e8c3c1a89d6837fa8885f7 Mon Sep 17 00:00:00 2001 From: Max Gfeller Date: Thu, 19 Dec 2013 08:47:55 +0100 Subject: [PATCH 016/638] Don't throw exception in the doDispatch method. --- src/Composer/EventDispatcher/EventDispatcher.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 2f19673c4..9c2aee91f 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -118,10 +118,6 @@ class EventDispatcher { $listeners = $this->getListeners($event); - if(sizeof($listeners) === 0) { - throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $event->getName())); - } - foreach ($listeners as $callable) { if (!is_string($callable) && is_callable($callable)) { call_user_func($callable, $event); From 067a8e764fdb281bd49cf20a03480c22725a716e Mon Sep 17 00:00:00 2001 From: Max Gfeller Date: Thu, 19 Dec 2013 08:48:41 +0100 Subject: [PATCH 017/638] Added a new method to check if an event has any listeners registered. --- src/Composer/EventDispatcher/EventDispatcher.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 9c2aee91f..6abb9162a 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -220,6 +220,19 @@ class EventDispatcher return call_user_func_array('array_merge', $listeners[$event->getName()]); } + /** + * Checks if an event has listeners registered + * + * @param Event $event + * @return boolean + */ + public function hasEventListeners(Event $event) + { + $listeners = $this->getListeners($event); + + return (sizeof($listeners) > 0); + } + /** * Finds all listeners defined as scripts in the package * From 8d10181873edcdf2475354def5e594e5e1ea9b3b Mon Sep 17 00:00:00 2001 From: Luis Cordova Date: Sat, 28 Dec 2013 18:03:28 -0500 Subject: [PATCH 018/638] documentation for http basic authentication method for repositories --- doc/articles/http-basic-authentication.md | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 doc/articles/http-basic-authentication.md diff --git a/doc/articles/http-basic-authentication.md b/doc/articles/http-basic-authentication.md new file mode 100644 index 000000000..c3d09fc34 --- /dev/null +++ b/doc/articles/http-basic-authentication.md @@ -0,0 +1,51 @@ + + +# Http basic authentication + +Your [satis](handling-private-packages-with-satis.md) server could be +secured with http basic authentication. In order to allow your project +to have access to these packages you will have to tell composer how to +authenticate with your credentials. + +The most simple way to provide your credentials is providing your set +of credentials inline with the repository specification such as: + + { + "repositories": [ + { + "type": "composer", + "url": "http://extremely:secret@repo.example.org" + } + ] + } + +This will basically teach composer how to authenticate automatically +when reading packages from the provided composer repository. + +This does not work for everybody especially when you don't want to +hard code your credentials into your composer.json. There is a second +way to provide these details and is via interaction. If you don't +provide the authentication credentials composer will prompt you upon +connection to enter the username and password. + +There is yet another way to provide these details and is via a file +`auth.json` inside your `COMPOSER_HOME` which looks something like +`/Users/username/.composer/auth.json` + + { + "basic-auth": [ + "repo.example1.org": { + "username": "my-username1", + "password": "my-secret-password1" + }, + "repo.example2.org": { + "username": "my-username2", + "password": "my-secret-password2" + } + ] + } + +This then will provide http basic authentication for two domains +serving packages with two different sets of credentials. From ee7961a0f30649033ac0ee5985ddfe68fbc391d8 Mon Sep 17 00:00:00 2001 From: James Moran Date: Sat, 11 Jan 2014 14:42:34 -0500 Subject: [PATCH 019/638] Implemented Pre/Post Archive Script Events The script events `pre-archive-cmd` and `post-archive-cmd` can be used to perform any tasks necessary when archiving the composer project. This functionality can be used to run unit test or and other build process before archiving, and can be used to distribute the archive when completed --- src/Composer/Command/ArchiveCommand.php | 10 +++++++++- src/Composer/Script/ScriptEvents.php | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 03e8600fa..7a57ca13b 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -17,6 +17,7 @@ use Composer\IO\IOInterface; use Composer\DependencyResolver\Pool; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\CompositeRepository; +use Composer\Script\ScriptEvents; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -55,13 +56,20 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - return $this->archive( + $this->getComposer()->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD); + + $returnCode = $this->archive( $this->getIO(), $input->getArgument('package'), $input->getArgument('version'), $input->getOption('format'), $input->getOption('dir') ); + + if (0 === $returnCode) { + $this->getComposer()->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD); + } + return $returnCode; } protected function archive(IOInterface $io, $packageName = null, $version = null, $format = 'tar', $dest = '.') diff --git a/src/Composer/Script/ScriptEvents.php b/src/Composer/Script/ScriptEvents.php index 5f3eaafd8..64ecbc8c5 100644 --- a/src/Composer/Script/ScriptEvents.php +++ b/src/Composer/Script/ScriptEvents.php @@ -165,4 +165,22 @@ class ScriptEvents */ const POST_CREATE_PROJECT_CMD = 'post-create-project-cmd'; + /** + * The PRE_ARCHIVE_CMD event occurs before the update command is executed. + * + * The event listener method receives a Composer\Script\CommandEvent instance. + * + * @var string + */ + const PRE_ARCHIVE_CMD = 'pre-archive-cmd'; + + /** + * The POST_ARCHIVE_CMD event occurs after the status command is executed. + * + * The event listener method receives a Composer\Script\CommandEvent instance. + * + * @var string + */ + const POST_ARCHIVE_CMD = 'post-archive-cmd'; + } From 57d1cff0bc1fa079eca1f56024d149a49d771db7 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 22 Jan 2014 17:41:53 +0100 Subject: [PATCH 020/638] Fixed a typo in the doc about github auth --- doc/articles/troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index e30b08cf8..2fc7bb487 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -112,7 +112,7 @@ for authentication asking your username and password so it can go ahead with its Unfortunately this will not work if you enabled two factor authentication on your GitHub account and to solve this issue you need to: -1. [Create](https://github.com/settings/applications) an oauthtoken on GitHub. +1. [Create](https://github.com/settings/applications) an oauth token on GitHub. [Read more](https://github.com/blog/1509-personal-api-tokens) on this. 2. Add it to the configuration running `composer config -g github-oauth.github.com ` From 23a61c5a1d392a108b22f995752d4319ae4e624e Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 22 Jan 2014 22:48:36 +0100 Subject: [PATCH 021/638] Fix typo in doc --- src/Composer/Downloader/GitDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 81030580f..a6056eec8 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -194,7 +194,7 @@ class GitDownloader extends VcsDownloader } /** - * Updates the given apth to the given commit ref + * Updates the given path to the given commit ref * * @param string $path * @param string $reference From aba201b4ba801c2c71b72389f1008c6fe11c399a Mon Sep 17 00:00:00 2001 From: Cinderella-Man Date: Thu, 23 Jan 2014 09:58:34 +0000 Subject: [PATCH 022/638] replaceVersion() method added --- src/Composer/Package/Package.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index b8a8252bc..f7ee0b1b7 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -493,4 +493,21 @@ class Package extends BasePackage { return $this->archiveExcludes; } + + /** + * Replaces current version and pretty version with passed values. + * It also sets stability. + * + * @param string $version + * + * @param string $prettyVersion + */ + public function replaceVersion($version, $prettyVersion) + { + $this->version = $version; + $this->prettyVersion = $prettyVersion; + + $this->stability = VersionParser::parseStability($version); + $this->dev = $this->stability === 'dev'; + } } From 990bea984564d7640917eb1434969a2c9ad69c86 Mon Sep 17 00:00:00 2001 From: Cinderella-Man Date: Thu, 23 Jan 2014 10:23:57 +0000 Subject: [PATCH 023/638] Comment fix --- src/Composer/Package/Package.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index f7ee0b1b7..ba3f611c1 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -498,9 +498,8 @@ class Package extends BasePackage * Replaces current version and pretty version with passed values. * It also sets stability. * - * @param string $version - * - * @param string $prettyVersion + * @param string $version The package's normalized version + * @param string $prettyVersion The package's non-normalized version */ public function replaceVersion($version, $prettyVersion) { From d07339382dc59a9e4757e9ce3b139bbc100eb224 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 22 Jan 2014 16:15:49 -0500 Subject: [PATCH 024/638] add lib-icu and hhvm --- doc/02-libraries.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 27428064f..10612c660 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -33,8 +33,11 @@ installed on the system but are not actually installable by Composer. This includes PHP itself, PHP extensions and some system libraries. * `php` represents the PHP version of the user, allowing you to apply - constraints, e.g. `>=5.4.0`. To require a 64bit version of php, you can - require the `php-64bit` package. + constraints, e.g. `>=5.4.0`. To require a 64bit version of php, you can + require the `php-64bit` package. + +* `hhvm` represents the version of the HHVM runtime (aka HipHop Virtual + Machine) and allows you to apply a constraint, e.g., '>=2.3.3'. * `ext-` allows you to require PHP extensions (includes core extensions). Versioning can be quite inconsistent here, so it's often @@ -42,8 +45,8 @@ includes PHP itself, PHP extensions and some system libraries. package name is `ext-gd`. * `lib-` allows constraints to be made on versions of libraries used by - PHP. The following are available: `curl`, `iconv`, `libxml`, `openssl`, - `pcre`, `uuid`, `xsl`. + PHP. The following are available: `curl`, `iconv`, `icu`, `libxml`, + `openssl`, `pcre`, `uuid`, `xsl`. You can use `composer show --platform` to get a list of your locally available platform packages. From 79a4c5cac10b0f96913c99513de12376860a86fe Mon Sep 17 00:00:00 2001 From: Pierre-yves Christmann Date: Mon, 27 Jan 2014 05:04:27 +0100 Subject: [PATCH 025/638] correct small bug, source files aren't installed and removed from the same path (problem with targetDir package attribute) --- src/Composer/Installer/LibraryInstaller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index b1677cec2..39dcd860e 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -178,7 +178,7 @@ class LibraryInstaller implements InstallerInterface protected function removeCode(PackageInterface $package) { - $downloadPath = $this->getPackageBasePath($package); + $downloadPath = $this->getInstallPath($package); $this->downloadManager->remove($package, $downloadPath); } From 3b25703f350496b9cdd2cd52f9d9b53b44ff673b Mon Sep 17 00:00:00 2001 From: Florian Weber Date: Tue, 28 Jan 2014 00:27:15 +0100 Subject: [PATCH 026/638] Replace %name% with %package% in 05-repositories.md --- doc/05-repositories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index ae947ea49..fdb7ce1a2 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -192,7 +192,7 @@ integrity, for example: The file above declares that acme/foo and acme/bar can be found in this repository, by loading the file referenced by `providers-url`, replacing -`%name%` by the package name and `%hash%` by the sha256 field. Those files +`%package%` by the package name and `%hash%` by the sha256 field. Those files themselves just contain package definitions as described [above](#packages). This field is optional. You probably don't need it for your own custom From c103b54991b5f5a3d8abfbd36589c32200004450 Mon Sep 17 00:00:00 2001 From: James Moran Date: Mon, 27 Jan 2014 19:45:20 -0500 Subject: [PATCH 027/638] updated documentation with pre/post archive events --- doc/articles/scripts.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index 06e7c784d..ff9c8b5bc 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -40,6 +40,8 @@ Composer fires the following named events during its execution process: installed, during the `create-project` command. - **post-create-project-cmd**: occurs after the `create-project` command is executed. +- **pre-archive-cmd**: occurs before the `archive` command is executed. +- **post-archive-cmd**: occurs after the `archive` command is executed. **NOTE: Composer makes no assumptions about the state of your dependencies prior to `install` or `update`. Therefore, you should not specify scripts that From 46776c8e23b4ffbdbd4727cbbfeee54c95eee4e7 Mon Sep 17 00:00:00 2001 From: Olivier Laviale Date: Wed, 29 Jan 2014 14:15:32 +0100 Subject: [PATCH 028/638] Improved package sorting --- src/Composer/Autoload/AutoloadGenerator.php | 102 ++++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2359265bd..5bc7cd383 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -611,45 +611,83 @@ FOOTER; protected function sortPackageMap(array $packageMap) { - $positions = array(); - $names = array(); - $indexes = array(); - - foreach ($packageMap as $position => $item) { - $mainName = $item[0]->getName(); - $names = array_merge(array_fill_keys($item[0]->getNames(), $mainName), $names); - $names[$mainName] = $mainName; - $indexes[$mainName] = $positions[$mainName] = $position; - } + $packages = array(); + $paths = array(); + $usageList = array(); foreach ($packageMap as $item) { - $position = $positions[$item[0]->getName()]; - foreach (array_merge($item[0]->getRequires(), $item[0]->getDevRequires()) as $link) { + list($package, $path) = $item; + $name = $package->getName(); + $packages[$name] = $package; + $paths[$name] = $path; + + foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { $target = $link->getTarget(); - if (!isset($names[$target])) { - continue; - } - - $target = $names[$target]; - if ($positions[$target] <= $position) { - continue; - } - - foreach ($positions as $key => $value) { - if ($value >= $position) { - break; - } - $positions[$key]--; - } - - $positions[$target] = $position - 1; + $usageList[$target][] = $name; } - asort($positions); } + $computing = array(); + $computed = array(); + $compute_importance = function($name) use(&$compute_importance, &$computing, &$computed, $usageList) { + # reusing computed importance + if (isset($computed[$name])) { + return $computed[$name]; + } + + # canceling circular dependency + if (isset($computing[$name])) { + return 0; + } + + $computing[$name] = true; + $weight = 0; + + if (isset($usageList[$name])) { + foreach ($usageList[$name] as $user) { + $weight -= 1 - $compute_importance($user); + } + } + + unset($computing[$name]); + $computed[$name] = $weight; + + return $weight; + }; + + $weightList = array(); + + foreach ($packages as $name => $package) { + $weight = $compute_importance($name); + $weightList[$name] = $weight; + } + + $stable_sort = function(&$array) { + static $transform, $restore; + + $i = 0; + + if (!$transform) { + $transform = function(&$v, $k) use(&$i) { + $v = array($v, ++$i, $k, $v); + }; + + $restore = function(&$v, $k) { + $v = $v[3]; + }; + } + + array_walk($array, $transform); + asort($array); + array_walk($array, $restore); + }; + + $stable_sort($weightList); + $sortedPackageMap = array(); - foreach (array_keys($positions) as $packageName) { - $sortedPackageMap[] = $packageMap[$indexes[$packageName]]; + + foreach (array_keys($weightList) as $name) { + $sortedPackageMap[] = array($packages[$name], $paths[$name]); } return $sortedPackageMap; From 17278999bac14290b088fd869ca98ec0d076e158 Mon Sep 17 00:00:00 2001 From: Olivier Laviale Date: Wed, 29 Jan 2014 14:36:34 +0100 Subject: [PATCH 029/638] Coding style compliance --- src/Composer/Autoload/AutoloadGenerator.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) mode change 100644 => 100755 src/Composer/Autoload/AutoloadGenerator.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php old mode 100644 new mode 100755 index 5bc7cd383..e63fe77ce --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -629,13 +629,13 @@ FOOTER; $computing = array(); $computed = array(); - $compute_importance = function($name) use(&$compute_importance, &$computing, &$computed, $usageList) { - # reusing computed importance + $computeImportance = function($name) use(&$computeImportance, &$computing, &$computed, $usageList) { + // reusing computed importance if (isset($computed[$name])) { return $computed[$name]; } - # canceling circular dependency + // canceling circular dependency if (isset($computing[$name])) { return 0; } @@ -645,7 +645,7 @@ FOOTER; if (isset($usageList[$name])) { foreach ($usageList[$name] as $user) { - $weight -= 1 - $compute_importance($user); + $weight -= 1 - $computeImportance($user); } } @@ -658,7 +658,7 @@ FOOTER; $weightList = array(); foreach ($packages as $name => $package) { - $weight = $compute_importance($name); + $weight = $computeImportance($name); $weightList[$name] = $weight; } From 3c75cf59e81620545854852c6efecdb63e174281 Mon Sep 17 00:00:00 2001 From: thewilkybarkid Date: Thu, 30 Jan 2014 09:08:56 +0000 Subject: [PATCH 030/638] Correct JSON --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 96641e8fe..c49fae602 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -407,7 +407,7 @@ Example: "autoload": { "psr-4": { "Monolog\\": "src/", - "Vendor\\Namespace\\": "", + "Vendor\\Namespace\\": "" } } } From 94635c0d1428cfb317e3850b02391009987cab54 Mon Sep 17 00:00:00 2001 From: Zbigniew Date: Thu, 30 Jan 2014 17:39:13 +0000 Subject: [PATCH 031/638] [tests] Unit tests for JsonValidationException class --- .../Test/Json/JsonValidationExceptionTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/Composer/Test/Json/JsonValidationExceptionTest.php diff --git a/tests/Composer/Test/Json/JsonValidationExceptionTest.php b/tests/Composer/Test/Json/JsonValidationExceptionTest.php new file mode 100644 index 000000000..38486a2a4 --- /dev/null +++ b/tests/Composer/Test/Json/JsonValidationExceptionTest.php @@ -0,0 +1,42 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Json; + +use Composer\Json\JsonValidationException; + +class JsonValidationExceptionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider errorProvider + */ + public function testGetErrors($message, $errors) + { + $object = new JsonValidationException($message, $errors); + $this->assertEquals($message, $object->getMessage()); + $this->assertEquals($errors, $object->getErrors()); + } + + public function testGetErrorsWhenNoErrorsProvided() + { + $object = new JsonValidationException('test message'); + $this->assertEquals(array(), $object->getErrors()); + } + + public function errorProvider() + { + return array( + array('test message', array()), + array(null, null) + ); + } +} From a4a9d0f0e499dad134b48e9e1ee0ae736f28cf2d Mon Sep 17 00:00:00 2001 From: Zbigniew Date: Thu, 30 Jan 2014 23:34:28 +0000 Subject: [PATCH 032/638] [Archiver] Rules in GitExcludeFilter are too broad --- .../Package/Archiver/BaseExcludeFilter.php | 2 +- .../Package/Archiver/GitExcludeFilterTest.php | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index bba2003a8..3c89919d2 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -140,7 +140,7 @@ abstract class BaseExcludeFilter $pattern .= '/'; } - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2); + $pattern .= substr(Finder\Glob::toRegex($rule), 2, -1); return array($pattern . '#', $negate, false); } diff --git a/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php new file mode 100644 index 000000000..a6d473a1e --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php @@ -0,0 +1,36 @@ + + * Jordi Boggiano + * + * 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\GitExcludeFilter; + +class GitExcludeFilterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider patterns + */ + public function testPatternEscape($ignore, $expected) + { + $filter = new GitExcludeFilter('/'); + + $this->assertEquals($expected, $filter->parseGitIgnoreLine($ignore)); + } + + public function patterns() + { + return array( + array('app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml$#', false, false)), + array('!app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml$#', true, false)), + ); + } +} From 5afe2f846fdd704848578a0f81f73791a5ea43f5 Mon Sep 17 00:00:00 2001 From: Zbigniew Date: Thu, 30 Jan 2014 23:42:11 +0000 Subject: [PATCH 033/638] [Archiver] comment for a confusing line --- src/Composer/Package/Archiver/BaseExcludeFilter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index 3c89919d2..f8c390e41 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -140,6 +140,7 @@ abstract class BaseExcludeFilter $pattern .= '/'; } + // remove delimiters as well as caret (^) from the regex $pattern .= substr(Finder\Glob::toRegex($rule), 2, -1); return array($pattern . '#', $negate, false); From 23d35204cd28374701b79dc4190b4661b3f81c5f Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 16:06:29 +0000 Subject: [PATCH 034/638] Bail out of the normal 401 handling routine when the origin is GitHub --- src/Composer/Util/RemoteFilesystem.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index d3ecec03d..53829d03a 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -247,6 +247,11 @@ class RemoteFilesystem throw new TransportException($message, 401); } + // GitHub requests bail out early to allow 2FA to be applied if requested. + if ('github.com' === $this->originUrl) { + throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', 401); + } + $this->promptAuthAndRetry(); break; } From 3f53acc9af3a998dbddbdf59c9cc52053c9ae29d Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 16:06:58 +0000 Subject: [PATCH 035/638] Test if the 401 was caused by 2FA and ask for OTP if appropriate --- src/Composer/Util/RemoteFilesystem.php | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 53829d03a..158ff8034 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -145,6 +145,42 @@ class RemoteFilesystem if ($e instanceof TransportException && !empty($http_response_header[0])) { $e->setHeaders($http_response_header); } + + // 401 when authentication was supplied, handle 2FA if required. + if ($e instanceof TransportException && 401 === $e->getCode() && $this->io->hasAuthentication($this->originUrl)) { + $headerNames = array_map(function($header) { + return strstr($header, ':', true); + }, $e->getHeaders()); + + if ($key = array_search('X-GitHub-OTP', $headerNames)) { + $headers = $e->getHeaders(); + list($required, $method) = explode(';', trim(substr(strstr($headers[$key], ':'), 1))); + + if ('required' === $required) { + $this->io->write('Two-factor Authentication'); + + if ('app' === $method) { + $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.'); + } + + if ('sms' === $method) { + // @todo + } + + $this->options['github-otp'] = trim($this->io->ask('Authentication Code: ')); + + $this->retry = true; + } + } else { + try { + $this->promptAuthAndRetry(); + } catch (TransportException $e) { + if ($e instanceof TransportException && !empty($http_response_header[0])) { + $e->setHeaders($http_response_header); + } + } + } + } } if ($errorMessage && !ini_get('allow_url_fopen')) { $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')'; From 360df90ba59a29c1ad03c75370a46327441a5664 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 16:07:26 +0000 Subject: [PATCH 036/638] Add GitHub OTP to request headers --- src/Composer/Util/RemoteFilesystem.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 158ff8034..26b2774c9 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -378,6 +378,13 @@ class RemoteFilesystem } } + // Handle GitHub two factor tokens. + if (isset($options['github-otp'])) { + $headers[] = 'X-GitHub-OTP: ' . $options['github-otp']; + + unset($options['github-otp']); + } + if (isset($options['http']['header']) && !is_array($options['http']['header'])) { $options['http']['header'] = explode("\r\n", trim($options['http']['header'], "\r\n")); } From 9a0f4392da69f05b09292c2f18f86f5ea86d1e29 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 16:11:45 +0000 Subject: [PATCH 037/638] Trim whitepsace from each argument --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 26b2774c9..890bd5aad 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -154,7 +154,7 @@ class RemoteFilesystem if ($key = array_search('X-GitHub-OTP', $headerNames)) { $headers = $e->getHeaders(); - list($required, $method) = explode(';', trim(substr(strstr($headers[$key], ':'), 1))); + list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); if ('required' === $required) { $this->io->write('Two-factor Authentication'); From 20dac3e836a02b57794559bb0501c356cd140459 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:31:56 +0000 Subject: [PATCH 038/638] Remove GitHub OTP code from RFS class --- src/Composer/Util/RemoteFilesystem.php | 48 -------------------------- 1 file changed, 48 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 890bd5aad..d3ecec03d 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -145,42 +145,6 @@ class RemoteFilesystem if ($e instanceof TransportException && !empty($http_response_header[0])) { $e->setHeaders($http_response_header); } - - // 401 when authentication was supplied, handle 2FA if required. - if ($e instanceof TransportException && 401 === $e->getCode() && $this->io->hasAuthentication($this->originUrl)) { - $headerNames = array_map(function($header) { - return strstr($header, ':', true); - }, $e->getHeaders()); - - if ($key = array_search('X-GitHub-OTP', $headerNames)) { - $headers = $e->getHeaders(); - list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); - - if ('required' === $required) { - $this->io->write('Two-factor Authentication'); - - if ('app' === $method) { - $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.'); - } - - if ('sms' === $method) { - // @todo - } - - $this->options['github-otp'] = trim($this->io->ask('Authentication Code: ')); - - $this->retry = true; - } - } else { - try { - $this->promptAuthAndRetry(); - } catch (TransportException $e) { - if ($e instanceof TransportException && !empty($http_response_header[0])) { - $e->setHeaders($http_response_header); - } - } - } - } } if ($errorMessage && !ini_get('allow_url_fopen')) { $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')'; @@ -283,11 +247,6 @@ class RemoteFilesystem throw new TransportException($message, 401); } - // GitHub requests bail out early to allow 2FA to be applied if requested. - if ('github.com' === $this->originUrl) { - throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', 401); - } - $this->promptAuthAndRetry(); break; } @@ -378,13 +337,6 @@ class RemoteFilesystem } } - // Handle GitHub two factor tokens. - if (isset($options['github-otp'])) { - $headers[] = 'X-GitHub-OTP: ' . $options['github-otp']; - - unset($options['github-otp']); - } - if (isset($options['http']['header']) && !is_array($options['http']['header'])) { $options['http']['header'] = explode("\r\n", trim($options['http']['header'], "\r\n")); } From 3f6a62099dd01e728b3215f968645d0c8d2237e7 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:32:55 +0000 Subject: [PATCH 039/638] Add an option which causes reauth attempts to be bypassed --- src/Composer/Util/RemoteFilesystem.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index d3ecec03d..67b429837 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -33,6 +33,7 @@ class RemoteFilesystem private $progress; private $lastProgress; private $options; + private $retryAuthFailure; /** * Constructor. @@ -109,12 +110,19 @@ class RemoteFilesystem $this->fileName = $fileName; $this->progress = $progress; $this->lastProgress = null; + $this->retryAuthFailure = true; // capture username/password from URL if there is one if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) { $this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2])); } + if (isset($additionalOptions['retry-auth-failure'])) { + $this->retryAuthFailure = (bool) $additionalOptions['retry-auth-failure']; + + unset($additionalOptions['retry-auth-failure']); + } + $options = $this->getOptionsForUrl($originUrl, $additionalOptions); if ($this->io->isDebug()) { @@ -247,6 +255,11 @@ class RemoteFilesystem throw new TransportException($message, 401); } + // Bail if the caller is going to handle authentication failures itself. + if (!$this->retryAuthFailure) { + throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', 401); + } + $this->promptAuthAndRetry(); break; } From be5e4b1589f74edc7414f1c8ed63b96ef9205204 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:37:07 +0000 Subject: [PATCH 040/638] Intercept auth rejections requiring an OTP token --- src/Composer/Util/GitHub.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 49e56f8c9..affae8c64 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -111,6 +111,34 @@ class GitHub ))); } catch (TransportException $e) { if (in_array($e->getCode(), array(403, 401))) { + // 401 when authentication was supplied, handle 2FA if required. + if ($this->io->hasAuthentication($originUrl)) { + $headerNames = array_map(function($header) { + return strstr($header, ':', true); + }, $e->getHeaders()); + + if ($key = array_search('X-GitHub-OTP', $headerNames)) { + $headers = $e->getHeaders(); + list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); + + if ('required' === $required) { + $this->io->write('Two-factor Authentication'); + + if ('app' === $method) { + $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.'); + } + + if ('sms' === $method) { + // @todo + } + + $otp = $this->io->ask('Authentication Code: '); + + continue; + } + } + } + $this->io->write('Invalid credentials.'); continue; } From 7e0d8c1bc5e098209cb7ee4f236c9962b897e3bb Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:38:14 +0000 Subject: [PATCH 041/638] Do not ask for credentials again if OTP token is present --- src/Composer/Util/GitHub.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index affae8c64..e3185745d 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -87,9 +87,13 @@ class GitHub $this->io->write('To revoke access to this token you can visit https://github.com/settings/applications'); while ($attemptCounter++ < 5) { try { - $username = $this->io->ask('Username: '); - $password = $this->io->askAndHideAnswer('Password: '); - $this->io->setAuthentication($originUrl, $username, $password); + if (empty($otp) || !$this->io->hasAuthentication($originUrl)) { + $username = $this->io->ask('Username: '); + $password = $this->io->askAndHideAnswer('Password: '); + $otp = null; + + $this->io->setAuthentication($originUrl, $username, $password); + } // build up OAuth app name $appName = 'Composer'; From cedae88b67e5a3940063536600d9bda20cf38f8c Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:38:43 +0000 Subject: [PATCH 042/638] Add OTP token to the request headers --- src/Composer/Util/GitHub.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index e3185745d..41fe63740 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -101,11 +101,17 @@ class GitHub $appName .= ' on ' . trim($output); } + $headers = array('Content-Type: application/json'); + + if ($otp) { + $headers[] = 'X-GitHub-OTP: ' . $otp; + } + $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( 'http' => array( 'method' => 'POST', 'follow_location' => false, - 'header' => "Content-Type: application/json\r\n", + 'header' => $headers, 'content' => json_encode(array( 'scopes' => array('repo'), 'note' => $appName, From 2a08f55079d85529277840e6d99d318319cf11e6 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 17 Jan 2014 15:39:05 +0000 Subject: [PATCH 043/638] Bypass RFS auth failure handling --- src/Composer/Util/GitHub.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 41fe63740..6794dd507 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -108,6 +108,7 @@ class GitHub } $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( + 'retry-auth-failure' => false, 'http' => array( 'method' => 'POST', 'follow_location' => false, From bcee7a04ee54e2d9b232a702c379eb0389255656 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Jan 2014 16:26:43 +0000 Subject: [PATCH 044/638] Add message when SMS authentication code is required --- src/Composer/Util/GitHub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 6794dd507..40060395f 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -140,7 +140,7 @@ class GitHub } if ('sms' === $method) { - // @todo + $this->io->write('You have been sent an SMS message with an authentication code to verify your identity.'); } $otp = $this->io->ask('Authentication Code: '); From f1af43068ce6e04f061d0593aaea8608d1432ae6 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Jan 2014 16:33:13 +0000 Subject: [PATCH 045/638] Change docs to reflect support for GitHub 2FA --- doc/04-schema.md | 2 +- doc/articles/troubleshooting.md | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 96641e8fe..635136e2b 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -684,7 +684,7 @@ The following options are supported: `{"github.com": "oauthtoken"}` as the value of this option will use `oauthtoken` to access private repositories on github and to circumvent the low IP-based rate limiting of their API. - [Read more](articles/troubleshooting.md#api-rate-limit-and-two-factor-authentication) + [Read more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) on how to get an oauth token for GitHub. * **vendor-dir:** Defaults to `vendor`. You can install dependencies into a different directory if you want to. diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index 2fc7bb487..a0b4565b6 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -105,12 +105,13 @@ Or, you can increase the limit with a command-line argument: or ```HKEY_CURRENT_USER\Software\Microsoft\Command Processor```. 3. Check if it contains any path to non-existent file, if it's the case, just remove them. -## API rate limit and two factor authentication +## API rate limit and oauth tokens Because of GitHub's rate limits on their API it can happen that Composer prompts for authentication asking your username and password so it can go ahead with its work. -Unfortunately this will not work if you enabled two factor authentication on -your GitHub account and to solve this issue you need to: + +If you would rather than provide your GitHub credentials to Composer you can +manually create a token using the following procedure: 1. [Create](https://github.com/settings/applications) an oauth token on GitHub. [Read more](https://github.com/blog/1509-personal-api-tokens) on this. From 0858e96ac685dcf6be98cb544cc36bac5e3d9741 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Jan 2014 16:33:47 +0000 Subject: [PATCH 046/638] Correct capitalisation of OAuth --- doc/04-schema.md | 2 +- doc/articles/troubleshooting.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 635136e2b..c48955ee6 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -685,7 +685,7 @@ The following options are supported: to access private repositories on github and to circumvent the low IP-based rate limiting of their API. [Read more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) - on how to get an oauth token for GitHub. + on how to get an OAuth token for GitHub. * **vendor-dir:** Defaults to `vendor`. You can install dependencies into a different directory if you want to. * **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index a0b4565b6..aa11715fc 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -105,7 +105,7 @@ Or, you can increase the limit with a command-line argument: or ```HKEY_CURRENT_USER\Software\Microsoft\Command Processor```. 3. Check if it contains any path to non-existent file, if it's the case, just remove them. -## API rate limit and oauth tokens +## API rate limit and OAuth tokens Because of GitHub's rate limits on their API it can happen that Composer prompts for authentication asking your username and password so it can go ahead with its work. @@ -113,7 +113,7 @@ for authentication asking your username and password so it can go ahead with its If you would rather than provide your GitHub credentials to Composer you can manually create a token using the following procedure: -1. [Create](https://github.com/settings/applications) an oauth token on GitHub. +1. [Create](https://github.com/settings/applications) an OAuth token on GitHub. [Read more](https://github.com/blog/1509-personal-api-tokens) on this. 2. Add it to the configuration running `composer config -g github-oauth.github.com ` From 78568b49d6cce8d282fd8d199650c0afde565c06 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Jan 2014 16:36:00 +0000 Subject: [PATCH 047/638] Correct use of English --- doc/articles/troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index aa11715fc..d3637cd03 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -110,7 +110,7 @@ Or, you can increase the limit with a command-line argument: Because of GitHub's rate limits on their API it can happen that Composer prompts for authentication asking your username and password so it can go ahead with its work. -If you would rather than provide your GitHub credentials to Composer you can +If you would prefer not to provide your GitHub credentials to Composer you can manually create a token using the following procedure: 1. [Create](https://github.com/settings/applications) an OAuth token on GitHub. From 8b7cdb7fb417f349eab04335d0a7bc3956343a81 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Jan 2014 16:42:49 +0000 Subject: [PATCH 048/638] Treat HTTP header as case insensitive --- src/Composer/Util/GitHub.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 40060395f..942e7749c 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -125,10 +125,10 @@ class GitHub // 401 when authentication was supplied, handle 2FA if required. if ($this->io->hasAuthentication($originUrl)) { $headerNames = array_map(function($header) { - return strstr($header, ':', true); + return strtolower(strstr($header, ':', true)); }, $e->getHeaders()); - if ($key = array_search('X-GitHub-OTP', $headerNames)) { + if ($key = array_search('x-github-otp', $headerNames)) { $headers = $e->getHeaders(); list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); From 9a87aa1aaf554743d7689fe289d9424fb3e1a630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Fri, 22 Nov 2013 09:44:44 +0100 Subject: [PATCH 049/638] added "home" command like the npm one, opens a package in your browser --- src/Composer/Command/HomeCommand.php | 138 +++++++++++++++++++++++++++ src/Composer/Console/Application.php | 1 + 2 files changed, 139 insertions(+) create mode 100644 src/Composer/Command/HomeCommand.php diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php new file mode 100644 index 000000000..376d99050 --- /dev/null +++ b/src/Composer/Command/HomeCommand.php @@ -0,0 +1,138 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\DependencyResolver\Pool; +use Composer\Factory; +use Composer\Package\CompletePackageInterface; +use Composer\Plugin\CommandEvent; +use Composer\Plugin\PluginEvents; +use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositoryInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\InvalidArgumentException; + +/** + * @author Robert Schönthal + */ +class HomeCommand extends Command +{ + protected $versionParser; + + protected function configure() + { + $this + ->setName('home') + ->setDescription('opens the package in your browser') + ->setDefinition(array( + new InputArgument('package', InputArgument::REQUIRED, 'Package to goto'), + )) + ->setHelp(<<initializeRepo($input, $output); + $package = $this->getPackage($repo, $input->getArgument('package')); + + /** @var CompletePackageInterface $package */ + if ($package instanceof CompletePackageInterface && filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { + $this->openBrowser($package->getSourceUrl()); + } elseif ($package instanceof CompletePackageInterface) { + $this->getIO()->write('no valid source-url given for ' . $package->getName()); + } else { + throw new InvalidArgumentException('package not found'); + } + } + + /** + * finds a package by name + * + * @param RepositoryInterface $repos + * @param string $name + * @return CompletePackageInterface + */ + protected function getPackage(RepositoryInterface $repos, $name) + { + $name = strtolower($name); + $pool = new Pool('dev'); + $pool->addRepository($repos); + $matches = $pool->whatProvides($name); + + foreach ($matches as $index => $package) { + // skip providers/replacers + if ($package->getName() !== $name) { + unset($matches[$index]); + continue; + } + + return $package; + } + } + + /** + * opens a url in your system default browser + * + * @param string $url + */ + private function openBrowser($url) + { + passthru('which xdg-open', $linux); + passthru('which open', $osx); + $windows = defined('PHP_WINDOWS_VERSION_MAJOR'); + + if (0 === $linux) { + passthru('xdg-open ' . $url); + } elseif (0 === $osx) { + passthru('open ' . $url); + } elseif (true === $windows) { + passthru('start "web" explorer "' . $url . '"'); + } else { + $this->getIO()->write('no suitable browser opening tool found, open yourself: ' . $url); + } + } + + /** + * initializes the repo + * + * @param InputInterface $input + * @param OutputInterface $output + * @return CompositeRepository + */ + private function initializeRepo(InputInterface $input, OutputInterface $output) + { + $composer = $this->getComposer(false); + + if ($composer) { + $repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); + } else { + $defaultRepos = Factory::createDefaultRepositories($this->getIO()); + $repo = new CompositeRepository($defaultRepos); + } + + if ($composer) { + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'home', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + + return $repo; + } + + return $repo; + } + +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 9d622cf67..cf11358d9 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -227,6 +227,7 @@ class Application extends BaseApplication $commands[] = new Command\RunScriptCommand(); $commands[] = new Command\LicensesCommand(); $commands[] = new Command\GlobalCommand(); + $commands[] = new Command\HomeCommand(); if ('phar:' === substr(__FILE__, 0, 5)) { $commands[] = new Command\SelfUpdateCommand(); From 25d062ccb49d3bd16c87b0ad1a5f935649460638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Fri, 22 Nov 2013 10:24:10 +0100 Subject: [PATCH 050/638] some minor refactorings --- src/Composer/Command/HomeCommand.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 376d99050..b1d3aac76 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -29,8 +29,9 @@ use Symfony\Component\Process\Exception\InvalidArgumentException; */ class HomeCommand extends Command { - protected $versionParser; - + /** + * {@inheritDoc} + */ protected function configure() { $this @@ -45,18 +46,20 @@ EOT ); } + /** + * {@inheritDoc} + */ protected function execute(InputInterface $input, OutputInterface $output) { $repo = $this->initializeRepo($input, $output); $package = $this->getPackage($repo, $input->getArgument('package')); - /** @var CompletePackageInterface $package */ - if ($package instanceof CompletePackageInterface && filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { - $this->openBrowser($package->getSourceUrl()); - } elseif ($package instanceof CompletePackageInterface) { - $this->getIO()->write('no valid source-url given for ' . $package->getName()); - } else { + if (!$package instanceof CompletePackageInterface) { throw new InvalidArgumentException('package not found'); + } elseif (filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { + $this->openBrowser($package->getSourceUrl()); + } else { + $output->writeln('no valid source-url given for ' . $package->getName()); } } @@ -92,18 +95,19 @@ EOT */ private function openBrowser($url) { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + return passthru('start "web" explorer "' . $url . '"'); + } + passthru('which xdg-open', $linux); passthru('which open', $osx); - $windows = defined('PHP_WINDOWS_VERSION_MAJOR'); if (0 === $linux) { passthru('xdg-open ' . $url); } elseif (0 === $osx) { passthru('open ' . $url); - } elseif (true === $windows) { - passthru('start "web" explorer "' . $url . '"'); } else { - $this->getIO()->write('no suitable browser opening tool found, open yourself: ' . $url); + $this->getIO()->write('no suitable browser opening command found, open yourself: ' . $url); } } From 325c57f30c8cb2078b824f01f16a88679d3f5d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Fri, 22 Nov 2013 11:45:27 +0100 Subject: [PATCH 051/638] tiny refactorings --- src/Composer/Command/HomeCommand.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index b1d3aac76..985ee61de 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -56,8 +56,11 @@ EOT if (!$package instanceof CompletePackageInterface) { throw new InvalidArgumentException('package not found'); - } elseif (filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { - $this->openBrowser($package->getSourceUrl()); + } + if (filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { + $support = $package->getSupport(); + $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); + $this->openBrowser($url); } else { $output->writeln('no valid source-url given for ' . $package->getName()); } From e36d7f23ef285afe5f7a7f3df8fa64ae750b540b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Sat, 23 Nov 2013 19:39:04 +0100 Subject: [PATCH 052/638] escape shell args --- src/Composer/Command/HomeCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 985ee61de..effe4a847 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -98,6 +98,8 @@ EOT */ private function openBrowser($url) { + $url = escapeshellarg($url); + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { return passthru('start "web" explorer "' . $url . '"'); } From 3a1edd3776ad232ca0ccc1133ed5ff04b3adc163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Fri, 31 Jan 2014 22:00:45 +0100 Subject: [PATCH 053/638] rename command --- src/Composer/Command/HomeCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index effe4a847..2192af147 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -35,7 +35,8 @@ class HomeCommand extends Command protected function configure() { $this - ->setName('home') + ->setName('browse') + ->setAliases(array('home')) ->setDescription('opens the package in your browser') ->setDefinition(array( new InputArgument('package', InputArgument::REQUIRED, 'Package to goto'), From 2b5a9aee6c780a8ab62f5f7d582fb15d4fd94883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Scho=CC=88nthal?= Date: Fri, 31 Jan 2014 22:09:55 +0100 Subject: [PATCH 054/638] some tiny refactorings --- src/Composer/Command/HomeCommand.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 2192af147..550f06308 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -15,8 +15,7 @@ namespace Composer\Command; use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\Package\CompletePackageInterface; -use Composer\Plugin\CommandEvent; -use Composer\Plugin\PluginEvents; +use Composer\Package\Loader\InvalidPackageException; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryInterface; use Symfony\Component\Console\Input\InputArgument; @@ -63,7 +62,7 @@ EOT $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); $this->openBrowser($url); } else { - $output->writeln('no valid source-url given for ' . $package->getName()); + throw new InvalidPackageException(array($package->getName() => 'invalid source-url')); } } @@ -135,13 +134,6 @@ EOT $repo = new CompositeRepository($defaultRepos); } - if ($composer) { - $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'home', $input, $output); - $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - - return $repo; - } - return $repo; } From 01cd990995d26724dbe6cfc75befbe6ea6f40f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Macias?= Date: Mon, 3 Feb 2014 16:53:56 +0100 Subject: [PATCH 055/638] Fix PSR-4 autoload classmap generator --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 6 ++++-- tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2359265bd..4f3ff3b61 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -168,7 +168,7 @@ EOF; $whitelist = sprintf( '{%s/%s.+(? $path) { if ('' === $namespace || 0 === strpos($class, $namespace)) { diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 499f4de0d..f0f428e9f 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -111,17 +111,19 @@ class AutoloadGeneratorTest extends TestCase ->will($this->returnValue(array())); $this->fs->ensureDirectoryExists($this->workingDir.'/composer'); - $this->fs->ensureDirectoryExists($this->workingDir.'/src'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src/Lala'); $this->fs->ensureDirectoryExists($this->workingDir.'/lib'); + file_put_contents($this->workingDir.'/src/Lala/ClassMapMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/src-fruit'); $this->fs->ensureDirectoryExists($this->workingDir.'/src-cake'); $this->fs->ensureDirectoryExists($this->workingDir.'/lib-cake'); + file_put_contents($this->workingDir.'/src-cake/ClassMapBar.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc'); file_put_contents($this->workingDir.'/composersrc/foo.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_1'); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); // Assert that autoload_namespaces.php was correctly generated. $this->assertAutoloadFiles('main', $this->vendorDir.'/composer'); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php index b7b2a9656..2fe8ee07a 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php @@ -6,5 +6,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Acme\\Cake\\ClassMapBar' => $baseDir . '/src-cake/ClassMapBar.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', + 'Lala\\ClassMapMain' => $baseDir . '/src/Lala/ClassMapMain.php', ); From a90a05a059ff6c0a30c40ec42348dd6fbafc1833 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 3 Feb 2014 23:02:33 +0100 Subject: [PATCH 056/638] Detect composer install foo/bar misuse and suggest alt --- src/Composer/Command/InstallCommand.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 955607c85..ac220fdf0 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -17,6 +17,7 @@ use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; /** @@ -43,7 +44,8 @@ class InstallCommand extends Command new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), - new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump') + new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), + new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp(<<install command reads the composer.lock file from @@ -60,6 +62,11 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { + if ($args = $input->getArgument('packages')) { + $output->writeln('Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.'); + return 1; + } + if ($input->getOption('no-custom-installers')) { $output->writeln('You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.'); $input->setOption('no-plugins', true); From 7566315c7af0213c6c8c0958150114263307b08d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 4 Feb 2014 12:24:59 +0100 Subject: [PATCH 057/638] Don't put dots in archive filenames they cause problems with PharData composer/satis#114 --- src/Composer/Package/Archiver/ArchiveManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 80865245e..a8891e0c7 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -72,7 +72,7 @@ class ArchiveManager */ public function getPackageFilename(PackageInterface $package) { - $nameParts = array(preg_replace('#[^a-z0-9-_.]#i', '-', $package->getName())); + $nameParts = array(preg_replace('#[^a-z0-9-_]#i', '-', $package->getName())); if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { $nameParts = array_merge($nameParts, array($package->getDistReference(), $package->getDistType())); From 084c11ef517339aeb80efca5485fda049203778c Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Tue, 4 Feb 2014 12:33:42 +0100 Subject: [PATCH 058/638] AutoloadGeneratorTest::testMainPackageAutoloadingWithTargetDir() - Remove PSR-4 from the package in target-dir test, since it is incompatible with target-dir. --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 499f4de0d..3dd979ee9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -198,10 +198,6 @@ class AutoloadGeneratorTest extends TestCase $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''), - 'psr-4' => array( - 'Acme\Fruit\\' => 'src-fruit/', - 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), - ), 'classmap' => array('Main/Foo/src', 'lib'), 'files' => array('foo.php', 'Main/Foo/bar.php'), )); From cab6943df0915c937340ee9a1e9163a203aa0001 Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Sun, 22 Dec 2013 21:34:31 +0100 Subject: [PATCH 059/638] AutoloadGeneratorTest::testOverrideVendorsAutoloading() - break autoload definitions to multiple lines. --- .../Test/Autoload/AutoloadGeneratorTest.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 3dd979ee9..230cdc28c 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -478,14 +478,22 @@ class AutoloadGeneratorTest extends TestCase public function testOverrideVendorsAutoloading() { $package = new Package('z', '1.0', '1.0'); - $package->setAutoload(array('psr-0' => array('A\\B' => $this->workingDir.'/lib'), 'classmap' => array($this->workingDir.'/src'))); + $package->setAutoload(array( + 'psr-0' => array('A\\B' => $this->workingDir.'/lib'), + 'classmap' => array($this->workingDir.'/src') + )); $package->setRequires(array(new Link('z', 'a/a'))); $packages = array(); $packages[] = $a = new Package('a/a', '1.0', '1.0'); $packages[] = $b = new Package('b/b', '1.0', '1.0'); - $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'), 'classmap' => array('classmap'))); - $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); + $a->setAutoload(array( + 'psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'), + 'classmap' => array('classmap'), + )); + $b->setAutoload(array( + 'psr-0' => array('B\\Sub\\Name' => 'src/'), + )); $this->repository->expects($this->once()) ->method('getCanonicalPackages') From bb26152de2b53e79fd7c64ccc5bab22e7160e9cd Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Sun, 22 Dec 2013 21:56:58 +0100 Subject: [PATCH 060/638] docblock comments on properties in AutoloadGeneratorTest --- .../Test/Autoload/AutoloadGeneratorTest.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 230cdc28c..76e913df5 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -19,16 +19,52 @@ use Composer\Package\AliasPackage; use Composer\Package\Package; use Composer\TestCase; use Composer\Script\ScriptEvents; +use Composer\Repository\InstalledRepositoryInterface; +use Composer\Installer\InstallationManager; +use Composer\Config; +use Composer\EventDispatcher\EventDispatcher; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class AutoloadGeneratorTest extends TestCase { + /** + * @var string + */ public $vendorDir; + + /** + * @var Config|MockObject + */ private $config; + + /** + * @var string + */ private $workingDir; + + /** + * @var InstallationManager|MockObject + */ private $im; + + /** + * @var InstalledRepositoryInterface|MockObject + */ private $repository; + + /** + * @var AutoloadGenerator + */ private $generator; + + /** + * @var Filesystem + */ private $fs; + + /** + * @var EventDispatcher|MockObject + */ private $eventDispatcher; protected function setUp() From 6a9fd1dd6057fc36706fbc2cd490e87ec67957c4 Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Sun, 22 Dec 2013 22:00:26 +0100 Subject: [PATCH 061/638] rename \$package to \$mainPackage in AutoloadGeneratorTest::testOverrideVendorsAutoloading() for clarity. --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 76e913df5..7702584ea 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -513,12 +513,12 @@ class AutoloadGeneratorTest extends TestCase public function testOverrideVendorsAutoloading() { - $package = new Package('z', '1.0', '1.0'); - $package->setAutoload(array( + $mainPackage = new Package('z', '1.0', '1.0'); + $mainPackage->setAutoload(array( 'psr-0' => array('A\\B' => $this->workingDir.'/lib'), 'classmap' => array($this->workingDir.'/src') )); - $package->setRequires(array(new Link('z', 'a/a'))); + $mainPackage->setRequires(array(new Link('z', 'a/a'))); $packages = array(); $packages[] = $a = new Package('a/a', '1.0', '1.0'); @@ -592,7 +592,7 @@ return array( EOF; - $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_9'); + $this->generator->dump($this->config, $this->repository, $mainPackage, $this->im, 'composer', true, '_9'); $this->assertEquals($expectedNamespace, file_get_contents($this->vendorDir.'/composer/autoload_namespaces.php')); $this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php')); $this->assertEquals($expectedClassmap, file_get_contents($this->vendorDir.'/composer/autoload_classmap.php')); From fcf8e7f5155468e5615edcbb66f030a069bac2db Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Wed, 8 Jan 2014 00:05:24 +0100 Subject: [PATCH 062/638] Random docblock improvements in AutoloadGeneratorTest. --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 7702584ea..f63d8c82b 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -511,6 +511,12 @@ class AutoloadGeneratorTest extends TestCase $this->assertTrue(function_exists('testFilesAutoloadOrderByDependencyRoot')); } + /** + * Test that PSR-0 and PSR-4 mappings are processed in the correct order for + * autoloading and for classmap generation: + * - The main package has priority over other packages. + * - Longer namespaces have priority over shorter namespaces. + */ public function testOverrideVendorsAutoloading() { $mainPackage = new Package('z', '1.0', '1.0'); @@ -542,8 +548,12 @@ class AutoloadGeneratorTest extends TestCase $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src'); $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/lib/A/B'); $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src'); + + // Define the classes A\B\C and Foo\Bar in the main package. file_put_contents($this->workingDir.'/lib/A/B/C.php', 'workingDir.'/src/classes.php', 'vendorDir.'/a/a/lib/A/B/C.php', 'vendorDir.'/a/a/classmap/classes.php', ' Date: Tue, 4 Feb 2014 20:25:39 +0100 Subject: [PATCH 063/638] scope isolation for user includes --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- src/Composer/Autoload/ClassLoader.php | 12 +++++++++++- .../Fixtures/autoload_real_files_by_dependency.php | 2 +- .../Autoload/Fixtures/autoload_real_functions.php | 2 +- .../Autoload/Fixtures/autoload_real_target_dir.php | 2 +- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2359265bd..65f81b5da 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -533,7 +533,7 @@ REGISTER_LOADER; $file .= <<<'INCLUDE_FILES' $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { - require $file; + \Composer\Autoload\includeFile($file); } diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index f438e319c..ef6761ef1 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -266,7 +266,7 @@ class ClassLoader public function loadClass($class) { if ($file = $this->findFile($class)) { - include $file; + includeFile($file); return true; } @@ -352,3 +352,13 @@ class ClassLoader return $this->classMap[$class] = false; } } + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile() +{ + include func_get_arg(0); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php index e58e8d2fa..928b5fb0c 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php @@ -45,7 +45,7 @@ class ComposerAutoloaderInitFilesAutoloadOrder $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { - require $file; + \Composer\Autoload\includeFile($file); } return $loader; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index a92e664cd..6caacb0e6 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -45,7 +45,7 @@ class ComposerAutoloaderInitFilesAutoload $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { - require $file; + \Composer\Autoload\includeFile($file); } return $loader; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php index 4a6259da2..f47584238 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -47,7 +47,7 @@ class ComposerAutoloaderInitTargetDir $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { - require $file; + \Composer\Autoload\includeFile($file); } return $loader; From 54b8831131a91acfa5b5fa401db56619be45cebb Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 5 Feb 2014 18:37:57 +0100 Subject: [PATCH 064/638] Revert "correct small bug, source files aren't installed and removed from the same path (problem with targetDir package attribute)" This reverts commit 79a4c5cac10b0f96913c99513de12376860a86fe. --- src/Composer/Installer/LibraryInstaller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 39dcd860e..b1677cec2 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -178,7 +178,7 @@ class LibraryInstaller implements InstallerInterface protected function removeCode(PackageInterface $package) { - $downloadPath = $this->getInstallPath($package); + $downloadPath = $this->getPackageBasePath($package); $this->downloadManager->remove($package, $downloadPath); } From 410181ee2992139a218df7e5cd6c7452e4e3753f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kerner?= Date: Thu, 16 Jan 2014 10:44:35 +0100 Subject: [PATCH 065/638] * added svn handling for tags, trunk, branches in root packages --- .../Package/Loader/RootPackageLoader.php | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index a067fa6a8..3ff018b5a 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -179,8 +179,15 @@ class RootPackageLoader extends ArrayLoader return $version; } - return $this->guessHgVersion($config); + $version = $this->guessHgVersion($config); + if (null !== $version) { + return $version; + } + + return $this->guessSvnVersion($config); } + + return null; } private function guessGitVersion(array $config) @@ -295,4 +302,45 @@ class RootPackageLoader extends ArrayLoader return $version; } + + private function guessSvnVersion(array $config) + { + // try to fetch current version from svn + if (0 === $this->process->execute('svn info --xml', $output)) { + + $regexDelimiter = '#'; + + $trunkPath = + isset($config['trunk-path']) + ? preg_quote($config['trunk-path'], $regexDelimiter) + : 'trunk'; + + $branchesPath = + isset($config['branches-path']) + ? preg_quote($config['branches-path'], $regexDelimiter) + : 'branches'; + + $tagsPath = + isset($config['tags-path']) + ? preg_quote($config['tags-path'], $regexDelimiter) + : 'tags'; + + $urlPattern = $regexDelimiter + . '.*/(' . $trunkPath . '|(' . $branchesPath . '|' . $tagsPath .')/(.*))' + . $regexDelimiter; + + if(preg_match($urlPattern, $output, $matches)) { + if(isset($matches[2]) && isset($matches[3]) && $branchesPath === $matches[2]) { + // we are in a branches path + $version = $this->versionParser->normalizeBranch($matches[3]); + if ('9999999-dev' === $version) { + $version = 'dev-' . $matches[3]; + } + return $version; + } + + return $this->versionParser->normalize(trim($matches[1])); + } + } + } } From 8dd05949837acbc1b916fa25fe9134f51acd7da6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 6 Feb 2014 00:33:46 +0100 Subject: [PATCH 066/638] Cleaner install instruction without curl --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 7b62fee16..64120fb43 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -119,7 +119,7 @@ Change to a directory on your `PATH` and run the install snippet to download composer.phar: C:\Users\username>cd C:\bin - C:\bin>php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));" + C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php > **Note:** If the above fails due to file_get_contents, use the `http` url or enable php_openssl.dll in php.ini From ed02997a7bb19c35999559dee765f53a7b6fb3be Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 6 Feb 2014 09:44:14 +0100 Subject: [PATCH 067/638] Fix mode and add docblock, refs #2644 --- src/Composer/Autoload/AutoloadGenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) mode change 100755 => 100644 src/Composer/Autoload/AutoloadGenerator.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php old mode 100755 new mode 100644 index ea0f69008..59c8a719c --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -609,6 +609,14 @@ FOOTER; return $autoloads; } + /** + * Sorts packages by dependency weight + * + * Packages of equal weight retain the original order + * + * @param array $packageMap + * @return array + */ protected function sortPackageMap(array $packageMap) { $packages = array(); From ed5da804dd90a95d362c5f1ce5b8443f31934a21 Mon Sep 17 00:00:00 2001 From: Julius Beckmann Date: Thu, 6 Feb 2014 10:50:06 +0100 Subject: [PATCH 068/638] Fixed handling of Metapackages in DownloadManager. The "getDownloaderForInstalledPackage" returns null for "metapackage" and the download(), update() and remove() methods did not handle this return value correctly. --- src/Composer/Downloader/DownloadManager.php | 13 +++- .../Test/Downloader/DownloadManagerTest.php | 78 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index e1eb1a9b7..ef6f79d5c 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -179,7 +179,9 @@ class DownloadManager $this->filesystem->ensureDirectoryExists($targetDir); $downloader = $this->getDownloaderForInstalledPackage($package); - $downloader->download($package, $targetDir); + if($downloader) { + $downloader->download($package, $targetDir); + } } /** @@ -194,6 +196,11 @@ class DownloadManager public function update(PackageInterface $initial, PackageInterface $target, $targetDir) { $downloader = $this->getDownloaderForInstalledPackage($initial); + if(!$downloader) { + + return; + } + $installationSource = $initial->getInstallationSource(); if ('dist' === $installationSource) { @@ -230,6 +237,8 @@ class DownloadManager public function remove(PackageInterface $package, $targetDir) { $downloader = $this->getDownloaderForInstalledPackage($package); - $downloader->remove($package, $targetDir); + if($downloader) { + $downloader->remove($package, $targetDir); + } } } diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index 29b2edf90..48242a818 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -182,6 +182,19 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->getDownloaderForInstalledPackage($package); } + public function testGetDownloaderForMetapackage() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('metapackage')); + + $manager = new DownloadManager(false, $this->filesystem); + + $this->assertNull($manager->getDownloaderForInstalledPackage($package)); + } + public function testFullPackageDownload() { $package = $this->createPackageMock(); @@ -308,6 +321,36 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->download($package, 'target_dir'); } + public function testMetapackagePackageDownload() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue(null)); + + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue(null)); // There is no downloader for Metapackages. + + $manager->download($package, 'target_dir'); + } + public function testFullPackageDownloadWithSourcePreferred() { $package = $this->createPackageMock(); @@ -598,6 +641,24 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->update($initial, $target, 'vendor/pkg'); } + public function testUpdateMetapackage() + { + $initial = $this->createPackageMock(); + $target = $this->createPackageMock(); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($initial) + ->will($this->returnValue(null)); // There is no downloader for metapackages. + + $manager->update($initial, $target, 'vendor/pkg'); + } + public function testRemove() { $package = $this->createPackageMock(); @@ -621,6 +682,23 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->remove($package, 'vendor/bundles/FOS/UserBundle'); } + public function testMetapackageRemove() + { + $package = $this->createPackageMock(); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue(null)); // There is no downloader for metapackages. + + $manager->remove($package, 'vendor/bundles/FOS/UserBundle'); + } + private function createDownloaderMock() { return $this->getMockBuilder('Composer\Downloader\DownloaderInterface') From 00a6f8e38d6402bb94b00e00238212d8338308d7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Feb 2014 11:53:42 +0100 Subject: [PATCH 069/638] dedicated scope isolation function for autoload_real --- src/Composer/Autoload/AutoloadGenerator.php | 13 +++++++++---- src/Composer/Autoload/ClassLoader.php | 4 ++-- .../Fixtures/autoload_real_files_by_dependency.php | 7 ++++++- .../Autoload/Fixtures/autoload_real_functions.php | 7 ++++++- .../Fixtures/autoload_real_include_path.php | 5 +++++ .../Autoload/Fixtures/autoload_real_target_dir.php | 7 ++++++- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 59c8a719c..c4c172898 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -530,10 +530,10 @@ REGISTER_AUTOLOAD; REGISTER_LOADER; if ($useIncludeFiles) { - $file .= <<<'INCLUDE_FILES' - $includeFiles = require __DIR__ . '/autoload_files.php'; - foreach ($includeFiles as $file) { - \Composer\Autoload\includeFile($file); + $file .= << Date: Thu, 6 Feb 2014 21:14:48 +0100 Subject: [PATCH 070/638] Fix doc note --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 64120fb43..4eeabddf2 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -121,7 +121,7 @@ composer.phar: C:\Users\username>cd C:\bin C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php -> **Note:** If the above fails due to file_get_contents, use the `http` url or enable php_openssl.dll in php.ini +> **Note:** If the above fails due to readfile, use the `http` url or enable php_openssl.dll in php.ini Create a new `composer.bat` file alongside `composer.phar`: From f2c48788b8e7f8878be61a23ee8253177839b660 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 6 Feb 2014 23:06:48 +0100 Subject: [PATCH 071/638] Reformat code to follow coding style --- .../Package/Loader/RootPackageLoader.php | 32 ++++--------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 3ff018b5a..8db725496 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -186,8 +186,6 @@ class RootPackageLoader extends ArrayLoader return $this->guessSvnVersion($config); } - - return null; } private function guessGitVersion(array $config) @@ -307,34 +305,18 @@ class RootPackageLoader extends ArrayLoader { // try to fetch current version from svn if (0 === $this->process->execute('svn info --xml', $output)) { + $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk'; + $branchesPath = isset($config['branches-path']) ? preg_quote($config['branches-path'], '#') : 'branches'; + $tagsPath = isset($config['tags-path']) ? preg_quote($config['tags-path'], '#') : 'tags'; - $regexDelimiter = '#'; - - $trunkPath = - isset($config['trunk-path']) - ? preg_quote($config['trunk-path'], $regexDelimiter) - : 'trunk'; + $urlPattern = '#.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))#'; - $branchesPath = - isset($config['branches-path']) - ? preg_quote($config['branches-path'], $regexDelimiter) - : 'branches'; - - $tagsPath = - isset($config['tags-path']) - ? preg_quote($config['tags-path'], $regexDelimiter) - : 'tags'; - - $urlPattern = $regexDelimiter - . '.*/(' . $trunkPath . '|(' . $branchesPath . '|' . $tagsPath .')/(.*))' - . $regexDelimiter; - - if(preg_match($urlPattern, $output, $matches)) { - if(isset($matches[2]) && isset($matches[3]) && $branchesPath === $matches[2]) { + if (preg_match($urlPattern, $output, $matches)) { + if (isset($matches[2]) && $branchesPath === $matches[2]) { // we are in a branches path $version = $this->versionParser->normalizeBranch($matches[3]); if ('9999999-dev' === $version) { - $version = 'dev-' . $matches[3]; + $version = 'dev-'.$matches[3]; } return $version; } From 0f95e531b8bcea7d4607b23bba4b22e6ed27ef72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kerner?= Date: Fri, 7 Feb 2014 10:41:40 +0100 Subject: [PATCH 072/638] * fixed svn tag directory handling --- src/Composer/Package/Loader/RootPackageLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 8db725496..5cedf9395 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -312,7 +312,7 @@ class RootPackageLoader extends ArrayLoader $urlPattern = '#.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))#'; if (preg_match($urlPattern, $output, $matches)) { - if (isset($matches[2]) && $branchesPath === $matches[2]) { + if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) { // we are in a branches path $version = $this->versionParser->normalizeBranch($matches[3]); if ('9999999-dev' === $version) { From ac78eaa02794a43f748485c283f1490b7f7b130e Mon Sep 17 00:00:00 2001 From: Gennady Feldman Date: Fri, 7 Feb 2014 11:11:36 -0500 Subject: [PATCH 073/638] Adding ssh protocol support to github-protocols. --- src/Composer/Command/ConfigCommand.php | 4 ++-- src/Composer/Config.php | 4 ++-- src/Composer/Downloader/GitDownloader.php | 9 +++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index bcd3beeab..48647512f 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -294,8 +294,8 @@ EOT } foreach ($vals as $val) { - if (!in_array($val, array('git', 'https'))) { - return 'valid protocols include: git, https'; + if (!in_array($val, array('git', 'https', 'ssh'))) { + return 'valid protocols include: git, https, ssh'; } } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 087949ef8..df006fd2e 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -24,7 +24,7 @@ class Config 'use-include-path' => false, 'preferred-install' => 'auto', 'notify-on-install' => true, - 'github-protocols' => array('git', 'https'), + 'github-protocols' => array('git', 'https', 'ssh'), 'vendor-dir' => 'vendor', 'bin-dir' => '{$vendor-dir}/bin', 'cache-dir' => '{$home}/cache', @@ -206,7 +206,7 @@ class Config case 'github-protocols': if (reset($this->config['github-protocols']) === 'http') { - throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https" or "git"'); + throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https" or "git" or "ssh"'); } return $this->config[$key]; diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index a6056eec8..ce02523ec 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -312,14 +312,19 @@ class GitDownloader extends VcsDownloader } // public github, autoswitch protocols - if (preg_match('{^(?:https?|git)(://'.$this->getGitHubDomainsRegex().'/.*)}', $url, $match)) { + if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/(.*)}', $url, $match)) { $protocols = $this->config->get('github-protocols'); if (!is_array($protocols)) { throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols)); } $messages = array(); foreach ($protocols as $protocol) { - $url = $protocol . $match[1]; + if ('ssh' === $protocol) { + $url = "git@" . $match[1] . ":" . $match[2]; + } else { + $url = $protocol ."://" . $match[1] . "/" . $match[2]; + } + if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput, $cwd)) { return; } From 3ad47b5d2cc19b67250c879cf0a19eee367313bf Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Mon, 10 Feb 2014 15:24:46 -0500 Subject: [PATCH 074/638] add allowing require to update dep chain, fixes composer/composer#2668 --- src/Composer/Command/RequireCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index f33c2fd00..4a005d809 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -42,6 +42,7 @@ class RequireCommand extends InitCommand new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), )) ->setHelp(<<setPreferDist($input->getOption('prefer-dist')) ->setDevMode(true) ->setUpdate(true) - ->setUpdateWhitelist(array_keys($requirements)); + ->setUpdateWhitelist(array_keys($requirements)) + ->setWhitelistDependencies($input->getOption('update-with-dependencies')); ; $status = $install->run(); From 81aa3a850c971ffedb00a316971ebcf71f4f5a8e Mon Sep 17 00:00:00 2001 From: Dave Hulbert Date: Tue, 11 Feb 2014 10:09:30 +0000 Subject: [PATCH 075/638] Fix some PHP doc blocks --- src/Composer/Autoload/ClassMapGenerator.php | 4 ++-- src/Composer/Config.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e9142c15f..73c036a37 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -24,7 +24,7 @@ class ClassMapGenerator /** * Generate a class map file * - * @param Traversable $dirs Directories or a single path to search in + * @param \Traversable $dirs Directories or a single path to search in * @param string $file The name of the class map file */ public static function dump($dirs, $file) @@ -41,7 +41,7 @@ class ClassMapGenerator /** * Iterate over all files in the given directory searching for classes * - * @param Iterator|string $path The path to search in or an iterator + * @param \Iterator|string $path The path to search in or an iterator * @param string $whitelist Regex that matches against the file path * * @return array A class map array diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 087949ef8..7cc1b6049 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -254,7 +254,7 @@ class Config /** * Replaces {$refs} inside a config string * - * @param string a config string that can contain {$refs-to-other-config} + * @param string $value a config string that can contain {$refs-to-other-config} * @return string */ private function process($value) From 44fd75ef383b01298a4acb108943a67920b5e890 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Wed, 12 Feb 2014 14:48:56 +0100 Subject: [PATCH 076/638] Fix Cache::gc() when COMPOSER_CACHE_DIR=/dev/null If we set COMPOSER_CACHE_DIR=/dev/null, and the garbage collector is triggered, we end up with the following error : The "/dev/null/" directory does not exist. This is because the Cache::gc() function does not check for Cache::enabled and instanciates a Finder unconditionnaly. Fix this by adding a check on Cache::enabled. --- src/Composer/Cache.php | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 6bdf43d5d..6fe01702b 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -144,28 +144,33 @@ class Cache public function gc($ttl, $maxSize) { - $expire = new \DateTime(); - $expire->modify('-'.$ttl.' seconds'); + if ($this->enabled) + { + $expire = new \DateTime(); + $expire->modify('-'.$ttl.' seconds'); - $finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s')); - foreach ($finder as $file) { - unlink($file->getRealPath()); - } - - $totalSize = $this->filesystem->size($this->root); - if ($totalSize > $maxSize) { - $iterator = $this->getFinder()->sortByAccessedTime()->getIterator(); - while ($totalSize > $maxSize && $iterator->valid()) { - $filepath = $iterator->current()->getRealPath(); - $totalSize -= $this->filesystem->size($filepath); - unlink($filepath); - $iterator->next(); + $finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s')); + foreach ($finder as $file) { + unlink($file->getRealPath()); } + + $totalSize = $this->filesystem->size($this->root); + if ($totalSize > $maxSize) { + $iterator = $this->getFinder()->sortByAccessedTime()->getIterator(); + while ($totalSize > $maxSize && $iterator->valid()) { + $filepath = $iterator->current()->getRealPath(); + $totalSize -= $this->filesystem->size($filepath); + unlink($filepath); + $iterator->next(); + } + } + + self::$cacheCollected = true; + + return true; } - self::$cacheCollected = true; - - return true; + return false; } public function sha1($file) From e03057156c9583db24f9aa64b6794aece3d3001f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Feb 2014 12:48:12 +0100 Subject: [PATCH 077/638] CS fixes --- src/Composer/Downloader/DownloadManager.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index ef6f79d5c..51649cc3a 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -179,7 +179,7 @@ class DownloadManager $this->filesystem->ensureDirectoryExists($targetDir); $downloader = $this->getDownloaderForInstalledPackage($package); - if($downloader) { + if ($downloader) { $downloader->download($package, $targetDir); } } @@ -196,8 +196,7 @@ class DownloadManager public function update(PackageInterface $initial, PackageInterface $target, $targetDir) { $downloader = $this->getDownloaderForInstalledPackage($initial); - if(!$downloader) { - + if (!$downloader) { return; } @@ -237,7 +236,7 @@ class DownloadManager public function remove(PackageInterface $package, $targetDir) { $downloader = $this->getDownloaderForInstalledPackage($package); - if($downloader) { + if ($downloader) { $downloader->remove($package, $targetDir); } } From e19da1435ef4806187fe6b8508bec89a120edf9d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Feb 2014 14:37:11 +0100 Subject: [PATCH 078/638] Update docs for the new flag --- doc/03-cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 8d34fb49e..3e9ae78e2 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -149,6 +149,8 @@ to the command. * **--no-update:** Disables the automatic update of the dependencies. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. +* **--update-with-dependencies** Also update dependencies of the newly + required packages. ## global From a25596698b1eaaf1fdf6297d2879b35aade9c88b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Feb 2014 14:40:09 +0100 Subject: [PATCH 079/638] Update short project description (creds to @briandoll) --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0e4e73b0..a954bcfc2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Composer - Dependency Management for PHP ======================================== -Composer is a dependency manager tracking local dependencies of your projects and libraries. +Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere. See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation. diff --git a/composer.json b/composer.json index f0d4ee56e..949342cd7 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "composer/composer", - "description": "Dependency Manager", + "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", "keywords": ["package", "dependency", "autoload"], "homepage": "http://getcomposer.org/", "type": "library", From f44aa64a2bbcd7295d0e39e056d74e79a6d7100b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Feb 2014 14:40:42 +0100 Subject: [PATCH 080/638] Update deps to sf 2.4.2 --- composer.lock | 92 ++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/composer.lock b/composer.lock index e09b12af3..c93fda28a 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "370b764a9317165e8ea7a2e1623e031b", + "hash": "fde56d480e03eef9e8c575b80cf5a09b", "packages": [ { "name": "justinrainbow/json-schema", @@ -79,17 +79,17 @@ }, { "name": "symfony/console", - "version": "v2.4.1", + "version": "v2.4.2", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "4c1ed2ff514bd85ee186eebb010ccbdeeab05af7" + "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/4c1ed2ff514bd85ee186eebb010ccbdeeab05af7", - "reference": "4c1ed2ff514bd85ee186eebb010ccbdeeab05af7", + "url": "https://api.github.com/repos/symfony/Console/zipball/940f217cbc3c8a33e5403e7c595495c4884400fe", + "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe", "shasum": "" }, "require": { @@ -119,7 +119,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -128,21 +130,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-01-01 08:14:50" + "time": "2014-02-11 13:52:09" }, { "name": "symfony/finder", - "version": "v2.4.1", + "version": "v2.4.2", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", "url": "https://github.com/symfony/Finder.git", - "reference": "6904345cf2b3bbab1f6d6e4ce1724cb99df9f00a" + "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/6904345cf2b3bbab1f6d6e4ce1724cb99df9f00a", - "reference": "6904345cf2b3bbab1f6d6e4ce1724cb99df9f00a", + "url": "https://api.github.com/repos/symfony/Finder/zipball/b6735d1fc16da13c4c7dddfe78366a4a098cf011", + "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011", "shasum": "" }, "require": { @@ -166,7 +168,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -175,21 +179,21 @@ ], "description": "Symfony Finder Component", "homepage": "http://symfony.com", - "time": "2014-01-01 08:14:50" + "time": "2014-01-07 13:28:54" }, { "name": "symfony/process", - "version": "v2.4.1", + "version": "v2.4.2", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "58fdccb311e44f28866f976c2d7b3227e9f713db" + "reference": "c175448bac997556f8ab972908a4e14c7291fb03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/58fdccb311e44f28866f976c2d7b3227e9f713db", - "reference": "58fdccb311e44f28866f976c2d7b3227e9f713db", + "url": "https://api.github.com/repos/symfony/Process/zipball/c175448bac997556f8ab972908a4e14c7291fb03", + "reference": "c175448bac997556f8ab972908a4e14c7291fb03", "shasum": "" }, "require": { @@ -213,7 +217,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -222,28 +228,28 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-01-05 02:10:50" + "time": "2014-02-11 13:52:09" } ], "packages-dev": [ { "name": "phpunit/php-code-coverage", - "version": "1.2.13", + "version": "1.2.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" + "reference": "6ba4ed2895d538a039d5d5866edc4ec0424c7852" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ba4ed2895d538a039d5d5866edc4ec0424c7852", + "reference": "6ba4ed2895d538a039d5d5866edc4ec0424c7852", "shasum": "" }, "require": { "php": ">=5.3.3", "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-text-template": ">=1.2.0@stable", "phpunit/php-token-stream": ">=1.1.3@stable" }, "require-dev": { @@ -285,7 +291,7 @@ "testing", "xunit" ], - "time": "2013-09-10 08:14:32" + "time": "2014-02-03 07:44:47" }, { "name": "phpunit/php-file-iterator", @@ -334,16 +340,16 @@ }, { "name": "phpunit/php-text-template", - "version": "1.1.4", + "version": "1.2.0", "source": { "type": "git", - "url": "git://github.com/sebastianbergmann/php-text-template.git", - "reference": "1.1.4" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" }, "dist": { "type": "zip", - "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4", - "reference": "1.1.4", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", "shasum": "" }, "require": { @@ -374,7 +380,7 @@ "keywords": [ "template" ], - "time": "2012-10-31 11:15:28" + "time": "2014-01-30 17:20:04" }, { "name": "phpunit/php-timer", @@ -472,16 +478,16 @@ }, { "name": "phpunit/phpunit", - "version": "3.7.28", + "version": "3.7.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d" + "reference": "d24e9877331039582497052cc3c4d9f465b88210" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", - "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d24e9877331039582497052cc3c4d9f465b88210", + "reference": "d24e9877331039582497052cc3c4d9f465b88210", "shasum": "" }, "require": { @@ -542,7 +548,7 @@ "testing", "xunit" ], - "time": "2013-10-17 07:27:40" + "time": "2014-02-03 07:46:27" }, { "name": "phpunit/phpunit-mock-objects", @@ -595,17 +601,17 @@ }, { "name": "symfony/yaml", - "version": "v2.4.1", + "version": "v2.4.2", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "4e1a237fc48145fae114b96458d799746ad89aa0" + "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/4e1a237fc48145fae114b96458d799746ad89aa0", - "reference": "4e1a237fc48145fae114b96458d799746ad89aa0", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/bb6ddaf8956139d1b8c360b4b713ed0138e876b3", + "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3", "shasum": "" }, "require": { @@ -629,7 +635,9 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { "name": "Symfony Community", @@ -638,7 +646,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2013-12-28 08:12:03" + "time": "2014-01-07 13:28:54" } ], "aliases": [ From 9896abeb380f65a146e988f51b46cdb8e1b88e01 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Feb 2014 16:23:53 +0100 Subject: [PATCH 081/638] Doc tweaks, refs #2682 --- doc/04-schema.md | 6 +++--- src/Composer/Config.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index c49fae602..6ebe378e4 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -676,10 +676,10 @@ The following options are supported: * **preferred-install:** Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option allows you to set the install method Composer will prefer to use. -* **github-protocols:** Defaults to `["git", "https"]`. A list of protocols to +* **github-protocols:** Defaults to `["git", "https", "ssh"]`. A list of protocols to use when cloning from github.com, in priority order. You can reconfigure it to - prioritize the https protocol if you are behind a proxy or have somehow bad - performances with the git protocol. + for example prioritize the https protocol if you are behind a proxy or have somehow + bad performances with the git protocol. * **github-oauth:** A list of domain names and oauth keys. For example using `{"github.com": "oauthtoken"}` as the value of this option will use `oauthtoken` to access private repositories on github and to circumvent the low IP-based diff --git a/src/Composer/Config.php b/src/Composer/Config.php index e390ae873..89f535725 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -206,7 +206,7 @@ class Config case 'github-protocols': if (reset($this->config['github-protocols']) === 'http') { - throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https" or "git" or "ssh"'); + throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https", "git" or "ssh"'); } return $this->config[$key]; From 7a902ed96d3225817bc88f7a62db1f5ebb531e3c Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 17 Feb 2014 13:54:35 +0100 Subject: [PATCH 082/638] Fix for #2613, when using php < 5.4 there was an unnecessary backslash before each utf-8 char. The problem was that the regexp matched all utf-8 encoded chars (included the ones that where escaped). The new regexp uses the lookbehind feature to check if the backslash isn't prefixed with an other backslash. --- src/Composer/Json/JsonFile.php | 2 +- tests/Composer/Test/Json/JsonFileTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 70b97b18d..594e0951b 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -231,7 +231,7 @@ class JsonFile if ($unescapeUnicode && function_exists('mb_convert_encoding')) { // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha - $buffer = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function($match) { + $buffer = preg_replace_callback('/(?assertJsonFormat('"\\u018c"', $data, 0); } + public function testDoubleEscapedUnicode() + { + $data = "Zdj\\u0119ciahl\\\\u0119kkjk"; + + $this->assertJsonFormat('"Zdj\\\\u0119ciahl\\\\\\\\u0119kkjk"', $data); + } + private function expectParseException($text, $json) { try { From f92f2f45a96df3c19ccdff827546d9a968537894 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Tue, 18 Feb 2014 09:01:12 +0100 Subject: [PATCH 083/638] Fix for #2613, when using php < 5.4 there was an unnecessary backslash before each utf-8 char. The problem was that the regexp matched all utf-8 encoded chars (included the ones that where escaped). The new regexp uses the lookbehind feature to check if the backslash isn't prefixed with an other backslash. --- src/Composer/Json/JsonFile.php | 81 +----------- src/Composer/Json/JsonFormatter.php | 123 ++++++++++++++++++ tests/Composer/Test/Json/JsonFileTest.php | 7 - .../Composer/Test/Json/JsonFormatterTest.php | 45 +++++++ 4 files changed, 169 insertions(+), 87 deletions(-) create mode 100644 src/Composer/Json/JsonFormatter.php create mode 100644 tests/Composer/Test/Json/JsonFormatterTest.php diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 594e0951b..2d35b9671 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -177,11 +177,6 @@ class JsonFile /** * Encodes an array into (optionally pretty-printed) JSON * - * This code is based on the function found at: - * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ - * - * Originally licensed under MIT by Dave Perrett - * * @param mixed $data Data to encode into a formatted JSON string * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) * @return string Encoded json @@ -202,81 +197,7 @@ class JsonFile return $json; } - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $outOfQuotes = true; - $buffer = ''; - $noescape = true; - - for ($i = 0; $i < $strLen; $i++) { - // Grab the next character in the string - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ('"' === $char && $noescape) { - $outOfQuotes = !$outOfQuotes; - } - - if (!$outOfQuotes) { - $buffer .= $char; - $noescape = '\\' === $char ? !$noescape : true; - continue; - } elseif ('' !== $buffer) { - if ($unescapeSlashes) { - $buffer = str_replace('\\/', '/', $buffer); - } - - if ($unescapeUnicode && function_exists('mb_convert_encoding')) { - // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha - $buffer = preg_replace_callback('/(? + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Json; + +/** + * Formats json strings used for php < 5.4 because the json_encode doesn't + * supports the flags JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE + * in these versions + * + * @author Konstantin Kudryashiv + * @author Jordi Boggiano + */ +class JsonFormatter +{ + /** + * + * This code is based on the function found at: + * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ + * + * Originally licensed under MIT by Dave Perrett + * + * + * @param string $json + * @param bool $unescapeUnicode Un escape unicode + * @param bool $unescapeSlashes Un escape slashes + * @return string + */ + public static function format($json, $unescapeUnicode, $unescapeSlashes) + { + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = ' '; + $newLine = "\n"; + $outOfQuotes = true; + $buffer = ''; + $noescape = true; + + for ($i = 0; $i < $strLen; $i++) { + // Grab the next character in the string + $char = substr($json, $i, 1); + + // Are we inside a quoted string? + if ('"' === $char && $noescape) { + $outOfQuotes = !$outOfQuotes; + } + + if (!$outOfQuotes) { + $buffer .= $char; + $noescape = '\\' === $char ? !$noescape : true; + continue; + } elseif ('' !== $buffer) { + if ($unescapeSlashes) { + $buffer = str_replace('\\/', '/', $buffer); + } + + if ($unescapeUnicode && function_exists('mb_convert_encoding')) { + // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha + $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function($match) { + $l = strlen($match[1]); + + if ($l%2) + return str_repeat ('\\', $l-1).mb_convert_encoding(pack('H*', $match[2]), 'UTF-8', 'UCS-2BE'); + + return $match[0]; + }, $buffer); + } + + $result .= $buffer.$char; + $buffer = ''; + continue; + } + + if (':' === $char) { + // Add a space after the : character + $char .= ' '; + } elseif (('}' === $char || ']' === $char)) { + $pos--; + $prevChar = substr($json, $i - 1, 1); + + if ('{' !== $prevChar && '[' !== $prevChar) { + // If this character is the end of an element, + // output a new line and indent the next line + $result .= $newLine; + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } else { + // Collapse empty {} and [] + $result = rtrim($result)."\n\n".$indentStr; + } + } + + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line + if (',' === $char || '{' === $char || '[' === $char) { + $result .= $newLine; + + if ('{' === $char || '[' === $char) { + $pos++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + } + + return $result; + } +} diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index ac8e83a16..79a1e40f4 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -198,13 +198,6 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase $this->assertJsonFormat('"\\u018c"', $data, 0); } - public function testDoubleEscapedUnicode() - { - $data = "Zdj\\u0119ciahl\\\\u0119kkjk"; - - $this->assertJsonFormat('"Zdj\\\\u0119ciahl\\\\\\\\u0119kkjk"', $data); - } - private function expectParseException($text, $json) { try { diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php new file mode 100644 index 000000000..60d1f0040 --- /dev/null +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -0,0 +1,45 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Json; + +use Composer\Json\JsonFormatter; + +class JsonFormatterTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test if \u0119 (196+153) will get correctly formatted + * See ticket #2613 + */ + public function testUnicodeWithPrependedSlash() + { + $data = '"' . chr(92) . chr(92) . chr(92) . 'u0119"'; + $encodedData = JsonFormatter::format($data, true, true); + $expected = '34+92+92+196+153+34'; + $this->assertEquals($expected, $this->getCharacterCodes($encodedData)); + } + + /** + * Convert string to character codes split by a plus sign + * @param string $string + * @return string + */ + protected function getCharacterCodes($string) + { + $codes = array(); + for ($i = 0; $i < strlen($string); $i++) { + $codes[] = ord($string[$i]); + } + return implode('+', $codes); + } + +} From 5d4900e79d5ea08118782345fb97e23e6ab1e4f0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 18 Feb 2014 13:28:46 +0100 Subject: [PATCH 084/638] Add --no-checkout flag to avoid checking out the default branch first, fixes #2717 --- src/Composer/Downloader/GitDownloader.php | 2 +- tests/Composer/Test/Downloader/GitDownloaderTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index ce02523ec..073554522 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -33,7 +33,7 @@ class GitDownloader extends VcsDownloader $ref = $package->getSourceReference(); $flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : ''; - $command = 'git clone %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; + $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); $commandCallable = function($url) use ($ref, $path, $command) { diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 7a0816eaf..01142580e 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -58,7 +58,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('dev-master')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $expectedGitCommand = $this->winCompat("git clone 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); $processExecutor->expects($this->at(0)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -97,13 +97,13 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1.0.0')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $expectedGitCommand = $this->winCompat("git clone 'git://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git://github.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git://github.com/composer/composer' && git fetch composer"); $processExecutor->expects($this->at(0)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(1)); - $expectedGitCommand = $this->winCompat("git clone 'https://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/composer/composer' && git fetch composer"); $processExecutor->expects($this->at(2)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -154,7 +154,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1.0.0')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $expectedGitCommand = $this->winCompat("git clone '{$protocol}://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer '{$protocol}://github.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout '{$protocol}://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer '{$protocol}://github.com/composer/composer' && git fetch composer"); $processExecutor->expects($this->at(0)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -182,7 +182,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase */ public function testDownloadThrowsRuntimeExceptionIfGitCommandFails() { - $expectedGitCommand = $this->winCompat("git clone 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getSourceReference') From beff1f5cc113dd645d74686b567f15ac7ff958b4 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 18 Feb 2014 23:28:45 +0100 Subject: [PATCH 085/638] This should create relative/absolute dist URLs depending on the way (relative/absolute) the artifact directory path was given. --- .../Repository/ArtifactRepository.php | 2 +- .../Repository/ArtifactRepositoryTest.php | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 869e4757f..6c87361c0 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -96,7 +96,7 @@ class ArtifactRepository extends ArrayRepository $package = JsonFile::parseJson($json, $composerFile); $package['dist'] = array( 'type' => 'zip', - 'url' => $file->getRealPath(), + 'url' => $file->getPathname(), 'reference' => $file->getBasename(), 'shasum' => sha1_file($file->getRealPath()) ); diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index 109b53bfb..65aeb1a76 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -16,6 +16,7 @@ use Composer\TestCase; use Composer\IO\NullIO; use Composer\Config; use Composer\Package\BasePackage; +use Composer\Util\Filesystem; class ArtifactRepositoryTest extends TestCase { @@ -40,4 +41,27 @@ class ArtifactRepositoryTest extends TestCase $this->assertSame($expectedPackages, $foundPackages); } + + public function testAbsoluteRepoUrlCreatesAbsoluteUrlPackages() + { + $absolutePath = __DIR__ . '/Fixtures/artifacts'; + $coordinates = array('type' => 'artifact', 'url' => $absolutePath); + $repo = new ArtifactRepository($coordinates, new NullIO(), new Config()); + + foreach ($repo->getPackages() as $package) { + $this->assertTrue(strpos($package->getDistUrl(), $absolutePath) === 0); + } + } + + public function testRelativeRepoUrlCreatesRelativeUrlPackages() + { + $relativePath = 'tests/Composer/Test/Repository/Fixtures/artifacts'; + $coordinates = array('type' => 'artifact', 'url' => $relativePath); + $repo = new ArtifactRepository($coordinates, new NullIO(), new Config()); + + foreach ($repo->getPackages() as $package) { + $this->assertTrue(strpos($package->getDistUrl(), $relativePath) === 0); + } + } + } From 9ac4ab1b85f53c82a66e5ba8c9a3f9f331f2e0b6 Mon Sep 17 00:00:00 2001 From: James Harris Date: Wed, 19 Feb 2014 11:05:05 +1000 Subject: [PATCH 086/638] Updated json-schema version requirement to allow newer versions. --- composer.json | 2 +- composer.lock | 65 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 949342cd7..b72856837 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.3.2", - "justinrainbow/json-schema": "1.1.*", + "justinrainbow/json-schema": "~1.1", "seld/jsonlint": "1.*", "symfony/console": "~2.3", "symfony/finder": "~2.2", diff --git a/composer.lock b/composer.lock index c93fda28a..3a46d45c4 100644 --- a/composer.lock +++ b/composer.lock @@ -3,32 +3,75 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "fde56d480e03eef9e8c575b80cf5a09b", + "hash": "78b771e9b9f3c0181350f1d6ed8fa3c7", "packages": [ { "name": "justinrainbow/json-schema", - "version": "1.1.0", + "version": "1.3.5", "source": { "type": "git", - "url": "git://github.com/justinrainbow/json-schema.git", - "reference": "v1.1.0" + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "01949f6d2130e9737ffae5d3952909a8de70d114" }, "dist": { "type": "zip", - "url": "https://github.com/justinrainbow/json-schema/zipball/v1.1.0", - "reference": "v1.1.0", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/01949f6d2130e9737ffae5d3952909a8de70d114", + "reference": "01949f6d2130e9737ffae5d3952909a8de70d114", "shasum": "" }, "require": { "php": ">=5.3.0" }, + "require-dev": { + "json-schema/json-schema-test-suite": "1.1.0", + "phpdocumentor/phpdocumentor": "~2", + "phpunit/phpunit": "~3.7" + }, + "bin": [ + "bin/validate-json" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, "autoload": { "psr-0": { "JsonSchema": "src/" } }, - "time": "2012-01-02 21:33:17" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch", + "homepage": "http://wiedler.ch/igor/" + }, + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com", + "homepage": "http://digitalkaoz.net" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2013-12-13 15:21:04" }, { "name": "seld/jsonlint", @@ -555,13 +598,13 @@ "version": "1.2.3", "source": { "type": "git", - "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "1.2.3" + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" }, "dist": { "type": "zip", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", - "reference": "1.2.3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", "shasum": "" }, "require": { From 432ace33d4216ecb3e3082d53cf06907dc329663 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 19 Feb 2014 10:55:00 +0100 Subject: [PATCH 087/638] Clean up svn environment to fix OSX issues, fixes #2708, refs #2146 --- src/Composer/Downloader/SvnDownloader.php | 6 ++++-- src/Composer/Package/Loader/RootPackageLoader.php | 3 +++ src/Composer/Repository/Vcs/SvnDriver.php | 2 ++ src/Composer/Util/Svn.php | 6 ++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index ec789c92a..f3ec821ba 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -26,8 +26,9 @@ class SvnDownloader extends VcsDownloader */ public function doDownload(PackageInterface $package, $path) { - $url = $package->getSourceUrl(); - $ref = $package->getSourceReference(); + SvnUtil::cleanEnv(); + $url = $package->getSourceUrl(); + $ref = $package->getSourceReference(); $this->io->write(" Checking out ".$package->getSourceReference()); $this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path); @@ -38,6 +39,7 @@ class SvnDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) { + SvnUtil::cleanEnv(); $url = $target->getSourceUrl(); $ref = $target->getSourceReference(); diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 5cedf9395..2dc4e220d 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -22,6 +22,7 @@ use Composer\Repository\Vcs\HgDriver; use Composer\IO\NullIO; use Composer\Util\ProcessExecutor; use Composer\Util\Git as GitUtil; +use Composer\Util\Svn as SvnUtil; /** * ArrayLoader built for the sole purpose of loading the root package @@ -303,6 +304,8 @@ class RootPackageLoader extends ArrayLoader private function guessSvnVersion(array $config) { + SvnUtil::cleanEnv(); + // try to fetch current version from svn if (0 === $this->process->execute('svn info --xml', $output)) { $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk'; diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index d69230cce..892afc3ed 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -51,6 +51,8 @@ class SvnDriver extends VcsDriver { $this->url = $this->baseUrl = rtrim(self::normalizeUrl($this->url), '/'); + SvnUtil::cleanEnv(); + if (isset($this->repoConfig['trunk-path'])) { $this->trunkPath = $this->repoConfig['trunk-path']; } diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 510b45daa..9db0930e6 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -69,6 +69,12 @@ class Svn $this->process = $process ?: new ProcessExecutor; } + public static function cleanEnv() + { + // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940 + putenv("DYLD_LIBRARY_PATH"); + } + /** * Execute an SVN command and try to fix up the process with credentials * if necessary. From 17ff26750584e4d1c14bef7316f170350321d922 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 19 Feb 2014 13:30:58 +0100 Subject: [PATCH 088/638] Update vendors --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 3a46d45c4..9e8b0b7ca 100644 --- a/composer.lock +++ b/composer.lock @@ -598,13 +598,13 @@ "version": "1.2.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" + "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "1.2.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", + "reference": "1.2.3", "shasum": "" }, "require": { From 3a2815b778ac3973614f131aff1b18321cc53b1b Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Wed, 19 Feb 2014 14:17:23 +0100 Subject: [PATCH 089/638] Added extra unit test from the example in the ticket. --- src/Composer/Json/JsonFormatter.php | 10 +++++++--- tests/Composer/Test/Json/JsonFileTest.php | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php index a7f3b2ba0..f06928e34 100644 --- a/src/Composer/Json/JsonFormatter.php +++ b/src/Composer/Json/JsonFormatter.php @@ -69,9 +69,13 @@ class JsonFormatter $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function($match) { $l = strlen($match[1]); - if ($l%2) - return str_repeat ('\\', $l-1).mb_convert_encoding(pack('H*', $match[2]), 'UTF-8', 'UCS-2BE'); - + if ($l % 2) { + return str_repeat('\\', $l - 1) . mb_convert_encoding( + pack('H*', $match[2]), + 'UTF-8', + 'UCS-2BE' + ); + } return $match[0]; }, $buffer); } diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 79a1e40f4..4c96720da 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -198,6 +198,18 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase $this->assertJsonFormat('"\\u018c"', $data, 0); } + public function testDoubleEscapedUnicode() + { + $jsonFile = new JsonFile('composer.json'); + $data = array("ZdjÄ™cia","hjkjhl\\u0119kkjk"); + $encodedData = $jsonFile->encode($data); + $doubleEncodedData = $jsonFile->encode(array('t' => $encodedData)); + + $decodedData = json_decode($doubleEncodedData, true); + $doubleData = json_decode($decodedData['t'], true); + $this->assertEquals($data, $doubleData); + } + private function expectParseException($text, $json) { try { From 41afc8324eb73232662e03aa7cf3b62a3b81d0b1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 19 Feb 2014 17:21:54 +0100 Subject: [PATCH 090/638] Workaround for json schema bug, fixes #2726 --- src/Composer/Json/JsonFile.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 70b97b18d..3e917f506 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -13,6 +13,7 @@ namespace Composer\Json; use JsonSchema\Validator; +use JsonSchema\Uri\UriRetriever; use Seld\JsonLint\JsonParser; use Seld\JsonLint\ParsingException; use Composer\Util\RemoteFilesystem; @@ -153,7 +154,8 @@ class JsonFile $schemaData = json_decode(file_get_contents($schemaFile)); if ($schema === self::LAX_SCHEMA) { - $schemaData->additionalProperties = true; + // TODO this should just be set to true, but this is a workaround for https://github.com/justinrainbow/json-schema/pull/94 + $schemaData->additionalProperties = (object) array('type' => array('object', 'string', 'array', 'number', 'null', 'boolean')); $schemaData->properties->name->required = false; $schemaData->properties->description->required = false; } From 40095d980c5f20b77b5311400b69adb878bd1bce Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Thu, 20 Feb 2014 16:26:34 +0100 Subject: [PATCH 091/638] Fix for #2494, don't retrieve svn log when the references don't contain a reference number. --- src/Composer/Downloader/SvnDownloader.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index ec789c92a..0ff5a0ede 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -144,14 +144,20 @@ class SvnDownloader extends VcsDownloader */ protected function getCommitLogs($fromReference, $toReference, $path) { - // strip paths from references and only keep the actual revision - $fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference); - $toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference); + if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference) ) { + // strip paths from references and only keep the actual revision + $fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference); + $toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference); - $command = sprintf('svn log -r%s:%s --incremental', $fromRevision, $toRevision); + $command = sprintf('svn log -r%s:%s --incremental', $fromRevision, $toRevision); - if (0 !== $this->process->execute($command, $output, $path)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + if (0 !== $this->process->execute($command, $output, $path)) { + throw new \RuntimeException( + 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput() + ); + } + } else { + $output = "Couldn't retrieve changes with reference $fromReference:$toReference"; } return $output; From 60bf5633eaaf340bc71e46a2b32a89ab6ae4482b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 20 Feb 2014 17:14:42 +0100 Subject: [PATCH 092/638] Wording tweaks, refs #2728 --- src/Composer/Downloader/SvnDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index f042e42bd..8094a00dd 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -159,7 +159,7 @@ class SvnDownloader extends VcsDownloader ); } } else { - $output = "Couldn't retrieve changes with reference $fromReference:$toReference"; + $output = "Could not retrieve changes between $fromReference and $toReference due to missing revision information"; } return $output; From 6bdcd9266c0d61eca6a30fc00fab1ae5e01cbe66 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Tue, 18 Feb 2014 10:38:13 +0100 Subject: [PATCH 093/638] Fixed #2601, the callback functions expect param 1 to be a reference to the $config --- src/Composer/Config/JsonConfigSource.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 5223eb5d2..506be613e 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -23,8 +23,10 @@ use Composer\Json\JsonManipulator; */ class JsonConfigSource implements ConfigSourceInterface { + /** + * @var \Composer\Json\JsonFile + */ private $file; - private $manipulator; /** * Constructor @@ -118,7 +120,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { // on failed clean update, call the fallback and rewrite the whole file $config = $this->file->read(); - array_unshift($args, $config); + $this->array_unshift_ref($args, $config); call_user_func_array($fallback, $args); $this->file->write($config); } @@ -127,4 +129,18 @@ class JsonConfigSource implements ConfigSourceInterface @chmod($this->file->getPath(), 0600); } } + + /** + * Prepend a reference to an element to the beginning of an array. + * + * @param array $array array + * @param mixed $value mixed + * @return array + */ + function array_unshift_ref(&$array, &$value) + { + $return = array_unshift($array, ''); + $array[0] =& $value; + return $return; + } } From d788ee7d994681264af58b68f95e9405d955cb8f Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Tue, 18 Feb 2014 10:47:06 +0100 Subject: [PATCH 094/638] Fixed docblock --- src/Composer/Config/JsonConfigSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 506be613e..1c12aadcf 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -133,8 +133,8 @@ class JsonConfigSource implements ConfigSourceInterface /** * Prepend a reference to an element to the beginning of an array. * - * @param array $array array - * @param mixed $value mixed + * @param array $array + * @param mixed $value * @return array */ function array_unshift_ref(&$array, &$value) From bc76e0014b59337d7b3f6b31e17daee3f4781797 Mon Sep 17 00:00:00 2001 From: Danack Date: Thu, 20 Feb 2014 17:30:51 +0000 Subject: [PATCH 095/638] Moved tests that are expected to work into their own directory. Added test for composer.json in incorrect directory. --- .../Repository/ArtifactRepositoryTest.php | 49 +++++++++++++++++- .../{ => correct}/composer-1.0.0-alpha6.zip | Bin .../{ => correct}/not-an-artifact.zip | Bin .../artifacts/{ => correct}/package0.zip | Bin .../artifacts/{ => correct}/package2.zip | Bin .../subfolder/not-an-artifact.zip | Bin .../{ => correct}/subfolder/package1.zip | Bin 7 files changed, 48 insertions(+), 1 deletion(-) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/composer-1.0.0-alpha6.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/not-an-artifact.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/package0.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/package2.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/subfolder/not-an-artifact.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{ => correct}/subfolder/package1.zip (100%) diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index 5ffae515a..f83ceee8c 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -26,9 +26,11 @@ class ArtifactRepositoryTest extends TestCase 'composer/composer-1.0.0-alpha6', 'vendor1/package2-4.3.2', 'vendor3/package1-5.4.3', + 'test/jsonInRoot-1.0.0', + 'test/jsonInFirstLevel-1.0.0' ); - $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts'); + $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts/correct'); $repo = new ArtifactRepository($coordinates, new NullIO(), new Config()); $foundPackages = array_map(function(BasePackage $package) { @@ -40,4 +42,49 @@ class ArtifactRepositoryTest extends TestCase $this->assertSame($expectedPackages, $foundPackages); } + + public function testExtractConfigFails() + { + $this->setExpectedException('RuntimeException', "Shouldn't have picked up composer.json from a location other than root or first level directory."); + + $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts/error/jsonWrongDirectory'); + new ArtifactRepository($coordinates, new NullIO(), new Config()); + } } + +//$archivesToCreate = array( +// 'jsonInRoot' => array( +// "extra.txt" => "Testing testing testing", +// "composer.json" => '{ "name": "test/jsonInRoot", "version": "1.0.0" }', +// "subdir/extra.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// ), +// +// 'jsonInFirstLevel' => array( +// "extra.txt" => "Testing testing testing", +// "subdir/composer.json" => '{ "name": "test/jsonInFirstLevel", "version": "1.0.0" }', +// "subdir/extra.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// ), +// +// 'jsonInSecondLevel' => array( +// "extra.txt" => "Testing testing testing", +// "subdir/extra1.txt" => "Testing testing testing", +// "subdir/foo/composer.json" => '{ "name": "test/jsonInSecondLevel", "version": "1.0.0" }', +// "subdir/foo/extra1.txt" => "Testing testing testing", +// "subdir/extra2.txt" => "Testing testing testing", +// "subdir/extra3.txt" => "Testing testing testing", +// ), +//); +// +//foreach($archivesToCreate as $archiveName => $fileDetails) { +// $zipFile = new ZipArchive(); +// $zipFile->open("$archiveName.zip", ZIPARCHIVE::CREATE); +// +// foreach ($fileDetails as $filename => $fileContents) { +// $zipFile->addFromString($filename, $fileContents); +// } +// +// $zipFile->close(); +//} + diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/composer-1.0.0-alpha6.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/composer-1.0.0-alpha6.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/composer-1.0.0-alpha6.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/composer-1.0.0-alpha6.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/not-an-artifact.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/not-an-artifact.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/not-an-artifact.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/not-an-artifact.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/package0.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/package0.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/package0.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/package0.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/package2.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/package2.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/package2.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/package2.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/not-an-artifact.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/not-an-artifact.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/not-an-artifact.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/not-an-artifact.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/package1.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/package1.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/package1.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/package1.zip From 40e484ed38d8df1dd6b854e7ee6af9993f76d130 Mon Sep 17 00:00:00 2001 From: Olivier Laviale Date: Thu, 20 Feb 2014 23:18:48 +0100 Subject: [PATCH 096/638] Fixed an issue that would lead to scattered autoloading files --- src/Composer/Autoload/AutoloadGenerator.php | 42 ++++++++----------- .../Fixtures/autoload_files_functions.php | 2 +- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c4c172898..ecb5a4c70 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -183,7 +183,6 @@ EOF; } } - $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); foreach ($autoloads['classmap'] as $dir) { foreach (ClassMapGenerator::createMap($dir) as $class => $path) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); @@ -360,7 +359,6 @@ EOF; protected function getIncludeFilesFile(array $files, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode) { $filesCode = ''; - $files = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($files)); foreach ($files as $functionFile) { $filesCode .= ' '.$this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile).",\n"; } @@ -580,33 +578,27 @@ FOOTER; foreach ($autoload[$type] as $namespace => $paths) { foreach ((array) $paths as $path) { - // remove target-dir from file paths of the root package - if ($type === 'files' && $package === $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { - $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); - $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); + if (($type === 'files' || $type === 'classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) + { + // remove target-dir from file paths of the root package + if ($package === $mainPackage) { + $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); + $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); + } + // add target-dir from file paths that don't have it + else { + $path = $package->getTargetDir() . '/' . $path; + } } - // add target-dir from file paths that don't have it - if ($type === 'files' && $package !== $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { - $path = $package->getTargetDir() . '/' . $path; + $relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path; + + if ($type === 'files' || $type === 'classmap') { + $autoloads[] = $relativePath; + continue; } - // remove target-dir from classmap entries of the root package - if ($type === 'classmap' && $package === $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { - $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); - $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); - } - - // add target-dir to classmap entries that don't have it - if ($type === 'classmap' && $package !== $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { - $path = $package->getTargetDir() . '/' . $path; - } - - if (empty($installPath)) { - $autoloads[$namespace][] = empty($path) ? '.' : $path; - } else { - $autoloads[$namespace][] = $installPath.'/'.$path; - } + $autoloads[$namespace][] = $relativePath; } } } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_files_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_files_functions.php index 0af7f8686..6b6494b2c 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_files_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_files_functions.php @@ -9,6 +9,6 @@ return array( $vendorDir . '/a/a/test.php', $vendorDir . '/b/b/test2.php', $vendorDir . '/c/c/foo/bar/test3.php', - $baseDir . '/root.php', $vendorDir . '/c/c/foo/bar/test4.php', + $baseDir . '/root.php', ); From 24aba5b51fc90d0f15cb21f4f00e55cd1937bc9d Mon Sep 17 00:00:00 2001 From: Danack Date: Fri, 21 Feb 2014 09:44:04 +0000 Subject: [PATCH 097/638] Moved file back to correct location. (+1 squashed commit) Squashed commits: [eec32aa] Updated detection to only allow composer.josn in root or first level dir. --- src/Composer/Repository/ArtifactRepository.php | 12 ++++++++++++ .../Test/Repository/ArtifactRepositoryTest.php | 14 ++++---------- .../{correct => }/composer-1.0.0-alpha6.zip | Bin .../Fixtures/artifacts/jsonInFirstLevel.zip | Bin 0 -> 542 bytes .../Repository/Fixtures/artifacts/jsonInRoot.zip | Bin 0 -> 522 bytes .../Fixtures/artifacts/jsonInSecondLevel.zip | Bin 0 -> 805 bytes .../artifacts/{correct => }/not-an-artifact.zip | Bin .../Fixtures/artifacts/{correct => }/package0.zip | Bin .../Fixtures/artifacts/{correct => }/package2.zip | Bin .../{correct => }/subfolder/not-an-artifact.zip | Bin .../{correct => }/subfolder/package1.zip | Bin 11 files changed, 16 insertions(+), 10 deletions(-) rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/composer-1.0.0-alpha6.zip (100%) create mode 100644 tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip create mode 100644 tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip create mode 100644 tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/not-an-artifact.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/package0.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/package2.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/subfolder/not-an-artifact.zip (100%) rename tests/Composer/Test/Repository/Fixtures/artifacts/{correct => }/subfolder/package1.zip (100%) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 769e35b40..34ed64ea3 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -88,6 +88,18 @@ class ArtifactRepository extends ArrayRepository for ($i = 0; $i < $zip->numFiles; $i++ ){ $stat = $zip->statIndex($i); if (strcmp(basename($stat['name']), $filename) === 0){ + $directoryName = dirname($stat['name']); + if ($directoryName == '.') { + //if composer.json is in root directory + //it has to be the one to use. + return $i; + } + + if(strpos($directoryName, '\\') !== false || + strpos($directoryName, '/') !== false) { + continue; + } + $length = strlen($stat['name']); if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) { //Check it's not a directory. diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index f83ceee8c..1958f5b9f 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -27,10 +27,12 @@ class ArtifactRepositoryTest extends TestCase 'vendor1/package2-4.3.2', 'vendor3/package1-5.4.3', 'test/jsonInRoot-1.0.0', - 'test/jsonInFirstLevel-1.0.0' + 'test/jsonInFirstLevel-1.0.0', + //The files not-an-artifact.zip and jsonSecondLevel are not valid + //artifacts and do not get detected. ); - $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts/correct'); + $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts'); $repo = new ArtifactRepository($coordinates, new NullIO(), new Config()); $foundPackages = array_map(function(BasePackage $package) { @@ -42,14 +44,6 @@ class ArtifactRepositoryTest extends TestCase $this->assertSame($expectedPackages, $foundPackages); } - - public function testExtractConfigFails() - { - $this->setExpectedException('RuntimeException', "Shouldn't have picked up composer.json from a location other than root or first level directory."); - - $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts/error/jsonWrongDirectory'); - new ArtifactRepository($coordinates, new NullIO(), new Config()); - } } //$archivesToCreate = array( diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/composer-1.0.0-alpha6.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/composer-1.0.0-alpha6.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/composer-1.0.0-alpha6.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/composer-1.0.0-alpha6.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInFirstLevel.zip new file mode 100644 index 0000000000000000000000000000000000000000..498037464ca08d944c1d9349b59ab1c19ae47004 GIT binary patch literal 542 zcmWIWW@Zs#U|`^2n7%B?CH~jPFdiUJ9EdrAIJKgrC{eGZqJ-O1SMTH*?{E#rrHlb6 zTKbq?p0)rg1!0gu#idCpnML}^`MCx8#i>PlS;hHztHS~UPI;g44c!zJpanGP;(4ue z=Rcn*KCPpr_t8_=`)uH)zyK|8U9EFx&NHtxykdO8I3Q>RD+8)c$c__0*hGXIk#!29 z=`_Oh9wQUPE7-#Xs2>TyZ4dB9)rKA}2tCF?Cbm#QHv!qzApau3Tp$x_0#5spb%Xqj c0M~&`WZmEp2=HcQ11Vtv!f!xYh!MmC05v{@hyVZp literal 0 HcmV?d00001 diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInRoot.zip new file mode 100644 index 0000000000000000000000000000000000000000..7b2a87eb9aea96dab1e4c2da86dff1332fd1de92 GIT binary patch literal 522 zcmWIWW@Zs#U|`^2n7%B?CH~jPFdiUJ9EdrAIJKgrC{eGZqJ-O1SMTH*?{E#rrHlb6 zTBIxrHW~qyf-o--C+FuD17q?=dBJ42sq_^#y50RP=FTDh>Pd7&Yk~!hWpfc z{lH6s0b1U=TIbH3XFe(QO7exIhUOI}22{h5-6H@rytp(eC9_DM2xlSd6hzZ$gy|zj zCI$xF!2r~c1mLy@c%y1V4+?}HV;~bHV9>Q9I~U|p1egG1qI(;kZjfgY;1rOFtQ+k0 T0B=?{kP;Rkd=I2q89_V%Pk?%l literal 0 HcmV?d00001 diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/jsonInSecondLevel.zip new file mode 100644 index 0000000000000000000000000000000000000000..0e5abc61b79424258c13ea520717c6327d1f3fc1 GIT binary patch literal 805 zcmWIWW@Zs#U|`^2n7%B?CH~jPFdiUJ9EdrAIJKgrC{eGZqJ-O1SMTH*?{E#rrHlbM zwFm;W6qhEYWESazbsFN=y8rr=W0pWIAS{8=Dohg>|bb?*G)e_1%$i15j4v1m1g&6Y z!0l2|RO^UwEpphPd(;TONAYPj#?;El#K3?%PJk{)0`M3L@W!nTBp_yB005NB#IFDV literal 0 HcmV?d00001 diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/not-an-artifact.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/not-an-artifact.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/not-an-artifact.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/not-an-artifact.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/package0.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/package0.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/package0.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/package0.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/package2.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/package2.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/package2.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/package2.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/not-an-artifact.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/not-an-artifact.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/not-an-artifact.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/not-an-artifact.zip diff --git a/tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/package1.zip b/tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/package1.zip similarity index 100% rename from tests/Composer/Test/Repository/Fixtures/artifacts/correct/subfolder/package1.zip rename to tests/Composer/Test/Repository/Fixtures/artifacts/subfolder/package1.zip From e4d4a7ae1734e4aaeca6b6b93d04543588a1871a Mon Sep 17 00:00:00 2001 From: Danack Date: Fri, 21 Feb 2014 09:49:12 +0000 Subject: [PATCH 098/638] Added comment for generating files. --- tests/Composer/Test/Repository/ArtifactRepositoryTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index 1958f5b9f..361f372ba 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -46,6 +46,8 @@ class ArtifactRepositoryTest extends TestCase } } +//Files jsonInFirstLevel.zip, jsonInRoot.zip and jsonInSecondLevel.zip were generated with: +// //$archivesToCreate = array( // 'jsonInRoot' => array( // "extra.txt" => "Testing testing testing", From 20a7dcd02c09168dfaabcf0b2048531ecaac1db0 Mon Sep 17 00:00:00 2001 From: Danack Date: Fri, 21 Feb 2014 09:54:42 +0000 Subject: [PATCH 099/638] Added explanation of why loop continues. --- src/Composer/Repository/ArtifactRepository.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 34ed64ea3..09efc0b9f 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -97,6 +97,7 @@ class ArtifactRepository extends ArrayRepository if(strpos($directoryName, '\\') !== false || strpos($directoryName, '/') !== false) { + //composer.json files below first directory are rejected continue; } From 3148ffd355349a46181540947567d67ef08bee0c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 12:25:15 +0100 Subject: [PATCH 100/638] Whitelist packages with names matching those specified before generating rules Addresses #2690 doesn't do any performance optimisations yet which we could do now --- .../DependencyResolver/DefaultPolicy.php | 4 +- src/Composer/DependencyResolver/Pool.php | 24 +++++-- src/Composer/DependencyResolver/Request.php | 2 +- .../DependencyResolver/RuleSetGenerator.php | 71 +++++++++++++++++++ .../Test/DependencyResolver/SolverTest.php | 10 +-- .../installer/provide-priorities.test | 34 --------- 6 files changed, 97 insertions(+), 48 deletions(-) delete mode 100644 tests/Composer/Test/Fixtures/installer/provide-priorities.test diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 190829213..a58cf6184 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -42,11 +42,11 @@ class DefaultPolicy implements PolicyInterface return $constraint->matchSpecific($version, true); } - public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package) + public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package, $mustMatchName = false) { $packages = array(); - foreach ($pool->whatProvides($package->getName()) as $candidate) { + foreach ($pool->whatProvides($package->getName(), null, $mustMatchName) as $candidate) { if ($candidate !== $package) { $packages[] = $candidate; } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index a1bba4f3a..8db4e90a0 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -50,6 +50,7 @@ class Pool protected $versionParser; protected $providerCache = array(); protected $filterRequires; + protected $whitelist = null; protected $id = 1; public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array()) @@ -66,6 +67,11 @@ class Pool $this->filterRequires = $filterRequires; } + public function setWhitelist($whitelist) + { + $this->whitelist = $whitelist; + } + /** * Adds a repository and its packages to this package pool * @@ -223,21 +229,24 @@ class Pool * @param string $name The package name to be searched for * @param LinkConstraintInterface $constraint A constraint that all returned * packages must match or null to return all + * @param bool $mustMatchName Whether the name of returned packages + * must match the given name * @return array A set of packages */ - public function whatProvides($name, LinkConstraintInterface $constraint = null) + public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false) { - if (isset($this->providerCache[$name][(string) $constraint])) { - return $this->providerCache[$name][(string) $constraint]; + $key = ((string) (int) $mustMatchName).((string) $constraint); + if (isset($this->providerCache[$name][$key])) { + return $this->providerCache[$name][$key]; } - return $this->providerCache[$name][(string) $constraint] = $this->computeWhatProvides($name, $constraint); + return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint, $mustMatchName); } /** * @see whatProvides */ - private function computeWhatProvides($name, $constraint) + private function computeWhatProvides($name, $constraint, $mustMatchName = false) { $candidates = array(); @@ -259,6 +268,9 @@ class Pool $nameMatch = false; foreach ($candidates as $candidate) { + if ($this->whitelist !== null && !isset($this->whitelist[$candidate->getId()])) { + continue; + } switch ($this->match($candidate, $name, $constraint)) { case self::MATCH_NONE: break; @@ -289,7 +301,7 @@ class Pool } // if a package with the required name exists, we ignore providers - if ($nameMatch) { + if ($nameMatch || $mustMatchName) { return $matches; } diff --git a/src/Composer/DependencyResolver/Request.php b/src/Composer/DependencyResolver/Request.php index 92c8aa175..85f83b4f5 100644 --- a/src/Composer/DependencyResolver/Request.php +++ b/src/Composer/DependencyResolver/Request.php @@ -46,7 +46,7 @@ class Request protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null) { $packageName = strtolower($packageName); - $packages = $this->pool->whatProvides($packageName, $constraint); + $packages = $this->pool->whatProvides($packageName, $constraint, true); $this->jobs[] = array( 'packages' => $packages, diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index b40ce1a60..d203832db 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -25,6 +25,8 @@ class RuleSetGenerator protected $rules; protected $jobs; protected $installedMap; + protected $whitelistedMap; + protected $addedMap; public function __construct(PolicyInterface $policy, Pool $pool) { @@ -141,6 +143,41 @@ class RuleSetGenerator $this->rules->add($newRule, $type); } + protected function whitelistFromPackage(PackageInterface $package) + { + $workQueue = new \SplQueue; + $workQueue->enqueue($package); + + while (!$workQueue->isEmpty()) { + $package = $workQueue->dequeue(); + if (isset($this->whitelistedMap[$package->getId()])) { + continue; + } + + $this->whitelistedMap[$package->getId()] = true; + + foreach ($package->getRequires() as $link) { + $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint(), true); + + foreach ($possibleRequires as $require) { + $workQueue->enqueue($require); + } + } + + $obsoleteProviders = $this->pool->whatProvides($package->getName(), null, true); + + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; + } + + if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { + $workQueue->enqueue($provider); + } + } + } + } + protected function addRulesForPackage(PackageInterface $package) { $workQueue = new \SplQueue; @@ -236,6 +273,30 @@ class RuleSetGenerator } } + private function whitelistFromUpdatePackages(PackageInterface $package) + { + $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package, true); + + foreach ($updates as $update) { + $this->whitelistFromPackage($update); + } + } + + protected function whitelistFromJobs() + { + foreach ($this->jobs as $job) { + switch ($job['cmd']) { + case 'install': + if ($job['packages']) { + foreach ($job['packages'] as $package) { + $this->whitelistFromPackage($package); + } + } + break; + } + } + } + protected function addRulesForJobs() { foreach ($this->jobs as $job) { @@ -270,6 +331,16 @@ class RuleSetGenerator $this->rules = new RuleSet; $this->installedMap = $installedMap; + $this->whitelistedNames = array(); + foreach ($this->installedMap as $package) { + $this->whitelistFromPackage($package); + $this->whitelistFromUpdatePackages($package); + } + $this->whitelistFromJobs(); + + $this->pool->setWhitelist($this->whitelistedMap); + + $this->addedMap = array(); foreach ($this->installedMap as $package) { $this->addRulesForPackage($package); $this->addRulesForUpdatePackages($package); diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 349f6e3b4..14f9c0943 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -441,10 +441,9 @@ class SolverTest extends TestCase $this->request->install('A'); - $this->checkSolverResult(array( - array('job' => 'install', 'package' => $packageQ), - array('job' => 'install', 'package' => $packageA), - )); + // must explicitly pick the provider, so error in this case + $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); + $this->solver->solve($this->request); } public function testSkipReplacerOfExistingPackage() @@ -574,11 +573,12 @@ class SolverTest extends TestCase $this->reposComplete(); $this->request->install('A'); + $this->request->install('C'); $this->checkSolverResult(array( - array('job' => 'install', 'package' => $packageB), array('job' => 'install', 'package' => $packageA), array('job' => 'install', 'package' => $packageC), + array('job' => 'install', 'package' => $packageB), )); } diff --git a/tests/Composer/Test/Fixtures/installer/provide-priorities.test b/tests/Composer/Test/Fixtures/installer/provide-priorities.test deleted file mode 100644 index f97e16e6c..000000000 --- a/tests/Composer/Test/Fixtures/installer/provide-priorities.test +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -Provide only applies when no existing package has the given name ---COMPOSER-- -{ - "repositories": [ - { - "type": "package", - "package": [ - { "name": "higher-prio-hijacker", "version": "1.1.0", "provide": { "package": "1.0.0" } }, - { "name": "provider2", "version": "1.1.0", "provide": { "package2": "1.0.0" } } - ] - }, - { - "type": "package", - "package": [ - { "name": "package", "version": "0.9.0" }, - { "name": "package", "version": "1.0.0" }, - { "name": "hijacker", "version": "1.1.0", "provide": { "package": "1.0.0" } }, - { "name": "provider3", "version": "1.1.0", "provide": { "package3": "1.0.0" } } - ] - } - ], - "require": { - "package": "1.*", - "package2": "1.*", - "provider3": "1.1.0" - } -} ---RUN-- -install ---EXPECT-- -Installing package (1.0.0) -Installing provider2 (1.1.0) -Installing provider3 (1.1.0) From ec12b8a675aa3eb4ed11809c95e2cf8e6ce804b3 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 13:14:36 +0100 Subject: [PATCH 101/638] Add tests for the changes in #2690 --- .../installer/broken-deps-do-not-replace.test | 25 +++++++++++++++++++ .../installer/replace-root-require.test | 24 ++++++++++++++++++ tests/Composer/Test/InstallerTest.php | 11 +++++--- 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test create mode 100644 tests/Composer/Test/Fixtures/installer/replace-root-require.test diff --git a/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test new file mode 100644 index 000000000..c626db198 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test @@ -0,0 +1,25 @@ +--TEST-- +Broken dependencies should not lead to a replacer being installed which is not mentioned by name +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" }, + { "name": "b/b", "version": "1.0.0", "require": {"c/c": "1.*"} }, + { "name": "c/c", "version": "1.0.0", "replace": {"a/a": "1.0.0" },"require":{"x/x": "1.0"}}, + { "name": "d/d", "version": "1.0.0", "replace": {"a/a": "1.0.0", "c/c":"1.0.0" }} + ] + } + ], + "require": { + "a/a": "1.*", + "b/b": "1.*" + } +} +--RUN-- +install +--EXPECT-EXIT-CODE-- +2 +--EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/replace-root-require.test b/tests/Composer/Test/Fixtures/installer/replace-root-require.test new file mode 100644 index 000000000..c00ac4fd5 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/replace-root-require.test @@ -0,0 +1,24 @@ +--TEST-- +Ensure a transiently required replacer can replace root requirements +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" }, + { "name": "b/b", "version": "1.0.0", "require": {"c/c": "1.*"} }, + { "name": "c/c", "version": "1.0.0", "replace": {"a/a": "1.0.0" }} + ] + } + ], + "require": { + "a/a": "1.*", + "b/b": "1.*" + } +} +--RUN-- +install +--EXPECT-- +Installing c/c (1.0.0) +Installing b/b (1.0.0) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 0cc17cfb0..9f2a1bc4b 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -138,7 +138,7 @@ class InstallerTest extends TestCase /** * @dataProvider getIntegrationTests */ - public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect) + public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode) { if ($condition) { eval('$res = '.$condition.';'); @@ -228,7 +228,7 @@ class InstallerTest extends TestCase $appOutput = fopen('php://memory', 'w+'); $result = $application->run(new StringInput($run), new StreamOutput($appOutput)); fseek($appOutput, 0); - $this->assertEquals(0, $result, $output . stream_get_contents($appOutput)); + $this->assertEquals($expectExitCode, $result, $output . stream_get_contents($appOutput)); if ($expectLock) { unset($actualLock['hash']); @@ -250,7 +250,7 @@ class InstallerTest extends TestCase $tests = array(); foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { - if (!preg_match('/\.test$/', $file)) { + if (!preg_match('/replace-root-require\.test$/', $file)) { continue; } @@ -266,6 +266,7 @@ class InstallerTest extends TestCase --RUN--\s*(?P.*?)\s* (?:--EXPECT-LOCK--\s*(?P'.$content.'))?\s* (?:--EXPECT-OUTPUT--\s*(?P'.$content.'))?\s* + (?:--EXPECT-EXIT-CODE--\s*(?P\d+))?\s* --EXPECT--\s*(?P.*?)\s* $}xs'; @@ -273,6 +274,7 @@ class InstallerTest extends TestCase $installedDev = array(); $lock = array(); $expectLock = array(); + $expectExitCode = 0; if (preg_match($pattern, $test, $match)) { try { @@ -294,6 +296,7 @@ class InstallerTest extends TestCase } $expectOutput = $match['expectOutput']; $expect = $match['expect']; + $expectExitCode = $match['expectExitCode']; } catch (\Exception $e) { die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); } @@ -301,7 +304,7 @@ class InstallerTest extends TestCase die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); } - $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect); + $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode); } return $tests; From bc7008270f13203835311022604260ae42d6bf20 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 13:15:54 +0100 Subject: [PATCH 102/638] Properly limit to name matches only if necessary --- src/Composer/DependencyResolver/Pool.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 8db4e90a0..56f356baa 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -300,8 +300,14 @@ class Pool } } + if ($mustMatchName) { + return array_filter($matches, function ($match) use ($name) { + return $match->getName() == $name; + }); + } + // if a package with the required name exists, we ignore providers - if ($nameMatch || $mustMatchName) { + if ($nameMatch) { return $matches; } From 5b80144ad007ef1a77d14d37fd78745dbce0b8b0 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 13:41:21 +0100 Subject: [PATCH 103/638] Resolve job packages after whitelist generation --- src/Composer/DependencyResolver/Problem.php | 21 +++++++++++++++---- src/Composer/DependencyResolver/Request.php | 4 +--- .../DependencyResolver/RuleSetGenerator.php | 17 ++++++++------- src/Composer/DependencyResolver/Solver.php | 5 +++-- .../Test/DependencyResolver/RequestTest.php | 10 ++++----- .../Test/DependencyResolver/SolverTest.php | 9 ++++---- tests/Composer/Test/InstallerTest.php | 4 ++-- 7 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 56ac867c4..765b74a19 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -80,7 +80,13 @@ class Problem $rule = $reason['rule']; $job = $reason['job']; - if ($job && $job['cmd'] === 'install' && empty($job['packages'])) { + if (isset($job['constraint'])) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + } else { + $packages = array(); + } + + if ($job && $job['cmd'] === 'install' && empty($packages)) { // handle php extensions if (0 === stripos($job['packageName'], 'ext-')) { $ext = substr($job['packageName'], 4); @@ -161,18 +167,25 @@ class Problem { switch ($job['cmd']) { case 'install': - if (!$job['packages']) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + if (!$packages) { return 'No package found to satisfy install request for '.$job['packageName'].$this->constraintToText($job['constraint']); } - return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($job['packages']).'.'; + return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($packages).'.'; case 'update': return 'Update request for '.$job['packageName'].$this->constraintToText($job['constraint']).'.'; case 'remove': return 'Removal request for '.$job['packageName'].$this->constraintToText($job['constraint']).''; } - return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->getPackageList($job['packages']).'])'; + if (isset($job['constraint'])) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + } else { + $packages = array(); + } + + return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->getPackageList($packages).'])'; } protected function getPackageList($packages) diff --git a/src/Composer/DependencyResolver/Request.php b/src/Composer/DependencyResolver/Request.php index 85f83b4f5..bf74318f6 100644 --- a/src/Composer/DependencyResolver/Request.php +++ b/src/Composer/DependencyResolver/Request.php @@ -46,10 +46,8 @@ class Request protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null) { $packageName = strtolower($packageName); - $packages = $this->pool->whatProvides($packageName, $constraint, true); $this->jobs[] = array( - 'packages' => $packages, 'cmd' => $cmd, 'packageName' => $packageName, 'constraint' => $constraint, @@ -58,7 +56,7 @@ class Request public function updateAll() { - $this->jobs[] = array('cmd' => 'update-all', 'packages' => array()); + $this->jobs[] = array('cmd' => 'update-all'); } public function getJobs() diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index d203832db..f555f1205 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -287,10 +287,9 @@ class RuleSetGenerator foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'install': - if ($job['packages']) { - foreach ($job['packages'] as $package) { - $this->whitelistFromPackage($package); - } + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint'], true); + foreach ($packages as $package) { + $this->whitelistFromPackage($package); } break; } @@ -302,21 +301,23 @@ class RuleSetGenerator foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'install': - if ($job['packages']) { - foreach ($job['packages'] as $package) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + if ($packages) { + foreach ($packages as $package) { if (!isset($this->installedMap[$package->getId()])) { $this->addRulesForPackage($package); } } - $rule = $this->createInstallOneOfRule($job['packages'], Rule::RULE_JOB_INSTALL, $job); + $rule = $this->createInstallOneOfRule($packages, Rule::RULE_JOB_INSTALL, $job); $this->addRule(RuleSet::TYPE_JOB, $rule); } break; case 'remove': // remove all packages with this name including uninstalled // ones to make sure none of them are picked as replacements - foreach ($job['packages'] as $package) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + foreach ($packages as $package) { $rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE, $job); $this->addRule(RuleSet::TYPE_JOB, $rule); } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 0fc860c72..3e101e0f3 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -131,7 +131,8 @@ class Solver foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'update': - foreach ($job['packages'] as $package) { + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); + foreach ($packages as $package) { if (isset($this->installedMap[$package->getId()])) { $this->updateMap[$package->getId()] = true; } @@ -145,7 +146,7 @@ class Solver break; case 'install': - if (!$job['packages']) { + if (!$this->pool->whatProvides($job['packageName'], $job['constraint'])) { $problem = new Problem($this->pool); $problem->addRule(new Rule($this->pool, array(), null, null, $job)); $this->problems[] = $problem; diff --git a/tests/Composer/Test/DependencyResolver/RequestTest.php b/tests/Composer/Test/DependencyResolver/RequestTest.php index d8cb865a0..0ba43ca73 100644 --- a/tests/Composer/Test/DependencyResolver/RequestTest.php +++ b/tests/Composer/Test/DependencyResolver/RequestTest.php @@ -39,9 +39,9 @@ class RequestTest extends TestCase $this->assertEquals( array( - array('packages' => array($foo), 'cmd' => 'install', 'packageName' => 'foo', 'constraint' => null), - array('packages' => array($bar), 'cmd' => 'install', 'packageName' => 'bar', 'constraint' => null), - array('packages' => array($foobar), 'cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null), + array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => null), + array('cmd' => 'install', 'packageName' => 'bar', 'constraint' => null), + array('cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null), ), $request->getJobs()); } @@ -66,7 +66,7 @@ class RequestTest extends TestCase $this->assertEquals( array( - array('packages' => array($foo1, $foo2), 'cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint), + array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint), ), $request->getJobs() ); @@ -80,7 +80,7 @@ class RequestTest extends TestCase $request->updateAll(); $this->assertEquals( - array(array('cmd' => 'update-all', 'packages' => array())), + array(array('cmd' => 'update-all')), $request->getJobs()); } } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 14f9c0943..ac78b5a26 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -464,7 +464,7 @@ class SolverTest extends TestCase )); } - public function testInstallReplacerOfMissingPackage() + public function testNoInstallReplacerOfMissingPackage() { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); @@ -475,10 +475,8 @@ class SolverTest extends TestCase $this->request->install('A'); - $this->checkSolverResult(array( - array('job' => 'install', 'package' => $packageQ), - array('job' => 'install', 'package' => $packageA), - )); + $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); + $this->solver->solve($this->request); } public function testSkipReplacedPackageIfReplacerIsSelected() @@ -611,6 +609,7 @@ class SolverTest extends TestCase $this->reposComplete(); $this->request->install('A'); + $this->request->install('D'); $this->checkSolverResult(array( array('job' => 'install', 'package' => $packageD2), diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 9f2a1bc4b..4e992c577 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -250,7 +250,7 @@ class InstallerTest extends TestCase $tests = array(); foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { - if (!preg_match('/replace-root-require\.test$/', $file)) { + if (!preg_match('/\.test$/', $file)) { continue; } @@ -296,7 +296,7 @@ class InstallerTest extends TestCase } $expectOutput = $match['expectOutput']; $expect = $match['expect']; - $expectExitCode = $match['expectExitCode']; + $expectExitCode = (int) $match['expectExitCode']; } catch (\Exception $e) { die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); } From eb5c785dcdf6727a88852571aefecb413f74e175 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 14:21:53 +0100 Subject: [PATCH 104/638] Remove superfluous string casts --- src/Composer/DependencyResolver/Pool.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 56f356baa..75b5aa7b2 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -235,7 +235,7 @@ class Pool */ public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false) { - $key = ((string) (int) $mustMatchName).((string) $constraint); + $key = ((int) $mustMatchName).$constraint; if (isset($this->providerCache[$name][$key])) { return $this->providerCache[$name][$key]; } From aa74818fe00c5f5eb57c1c38b807e9e2950c670c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 16:52:27 +0100 Subject: [PATCH 105/638] Handle array candidates in whatProvides --- src/Composer/DependencyResolver/Pool.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 75b5aa7b2..18f2d5797 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -268,7 +268,10 @@ class Pool $nameMatch = false; foreach ($candidates as $candidate) { - if ($this->whitelist !== null && !isset($this->whitelist[$candidate->getId()])) { + if ($this->whitelist !== null && ( + (is_array($candidate) && isset($candidate['id']) && !isset($this->whitelist[$candidate['id']])) || + (is_object($candidate) && !isset($this->whitelist[$candidate->getId()])) + )) { continue; } switch ($this->match($candidate, $name, $constraint)) { From 4af421bce157992952ee221fdb2e868e00531e11 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 22 Feb 2014 17:30:47 +0100 Subject: [PATCH 106/638] Update aliases.md added another example to make clear how it works. fixes #2741 --- doc/articles/aliases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index 26a9c46ab..9e8f3da89 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -7,7 +7,7 @@ ## Why aliases? When you are using a VCS repository, you will only get comparable versions for -branches that look like versions, such as `2.0`. For your `master` branch, you +branches that look like versions, such as `2.0` or `2.0.x`. For your `master` branch, you will get a `dev-master` version. For your `bugfix` branch, you will get a `dev-bugfix` version. From 714a47ef93d2387b66a6eff2cb9e2191c79b8d6d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 23 Feb 2014 17:13:38 +0100 Subject: [PATCH 107/638] Fix detached head handling for non-committish sources, fixes #2732 --- src/Composer/Package/Loader/RootPackageLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 2dc4e220d..f177f8eab 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -210,7 +210,7 @@ class RootPackageLoader extends ArrayLoader // find current branch and collect all branch names foreach ($this->process->splitLines($output) as $branch) { - if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from [a-f0-9]+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { + if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ') { $version = 'dev-'.$match[2]; $isFeatureBranch = true; From e8a3fc5c1c12203b7dc38406e73331f871c5f56e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 08:24:01 +0100 Subject: [PATCH 108/638] Run tests on PHP 5.6 on travis too --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 800a4f2f1..576b2bcbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ php: - 5.3 - 5.4 - 5.5 + - 5.6 - hhvm matrix: From eed5ba7752c0151887d2c5c8cd33cbecdee31b5c Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 24 Feb 2014 09:40:02 +0100 Subject: [PATCH 109/638] Updated the troubleshooting doc with a chapter for the proc_fork errors that happen on a tiny VPS with no swap space. Also added the solution provided in #1849, #1101, #945. --- doc/articles/troubleshooting.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index 2fc7bb487..fc9faa8d5 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -118,3 +118,22 @@ your GitHub account and to solve this issue you need to: 2. Add it to the configuration running `composer config -g github-oauth.github.com ` Now Composer should install/update without asking for authentication. + +## proc_open(): fork failed errors +If composer shows proc_open() fork failed on some commands: + + PHP Fatal error: Uncaught exception 'ErrorException' with message 'proc_open(): fork failed - Cannot allocate memory' in phar + +This could be happening because the VPS runs out of memory and has no Swap space enabled. + + [root@my_tiny_vps htdocs]# free -m + total used free shared buffers cached + Mem: 2048 357 1690 0 0 237 + -/+ buffers/cache: 119 1928 + Swap: 0 0 0 + +To enable the swap you can use for example: + + /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 + /sbin/mkswap /var/swap.1 + /sbin/swapon /var/swap.1 From 2c01c9dd05db405dd756d3f841ffab73d738ef5a Mon Sep 17 00:00:00 2001 From: Richard George Date: Mon, 24 Feb 2014 11:28:08 +0000 Subject: [PATCH 110/638] Avoid "Package foo/* listed for update is not installed" error Previously 'compose update foo/*' gave an error "Package foo/* listed for update is not installed. Ignoring" even if some foo/* packages were present; however the packages *would* then be updated as requested. This removes the false error iff foo/SOMEPACKAGE is required. --- src/Composer/Installer.php | 41 ++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e1dbd496f..358d35d94 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -105,6 +105,11 @@ class Installer protected $verbose = false; protected $update = false; protected $runScripts = true; + /** + * Array of package names/globs flagged for update + * + * @var array|null + */ protected $updateWhitelist = null; protected $whitelistDependencies = false; @@ -785,9 +790,8 @@ class Installer } foreach ($this->updateWhitelist as $whiteListedPattern => $void) { - $cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern)); - - if (preg_match("{^".$cleanedWhiteListedPattern."$}i", $package->getName())) { + $patternRegexp = $this->packageNameToRegexp($whiteListedPattern); + if (preg_match($patternRegexp, $package->getName())) { return true; } } @@ -795,6 +799,19 @@ class Installer return false; } + /** + * Build a regexp from a package name, expanding * globs as required + * + * @param $whiteListedPattern + * @return string + */ + private function packageNameToRegexp($whiteListedPattern) + { + $cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern)); + $patternRegexp = "{^" . $cleanedWhiteListedPattern . "$}i"; + return $patternRegexp; + } + private function extractPlatformRequirements($links) { $platformReqs = array(); @@ -844,11 +861,27 @@ class Installer $seen = array(); + $rootRequiredPackageNames = array_keys($rootRequires); + foreach ($this->updateWhitelist as $packageName => $void) { $packageQueue = new \SplQueue; $depPackages = $pool->whatProvides($packageName); - if (count($depPackages) == 0 && !in_array($packageName, $requiredPackageNames) && !in_array($packageName, array('nothing', 'lock'))) { + + $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames); + + if (!$nameMatchesRequiredPackage) { + //maybe the name is a glob or similar that won't match directly + $whitelistPatternRegexp = $this->packageNameToRegexp($packageName); + foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { + if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { + $nameMatchesRequiredPackage = true; + break; + } + } + } + + if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock'))) { $this->io->write('Package "' . $packageName . '" listed for update is not installed. Ignoring.'); } From 234be0b5e31b2e590c69824ac7239b185e42360c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 24 Feb 2014 12:49:09 +0100 Subject: [PATCH 111/638] CS fixes, refs #2750 --- src/Composer/Installer.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 358d35d94..4e4502dfe 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -802,14 +802,14 @@ class Installer /** * Build a regexp from a package name, expanding * globs as required * - * @param $whiteListedPattern + * @param string $whiteListedPattern * @return string */ private function packageNameToRegexp($whiteListedPattern) { $cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern)); - $patternRegexp = "{^" . $cleanedWhiteListedPattern . "$}i"; - return $patternRegexp; + + return "{^" . $cleanedWhiteListedPattern . "$}i"; } private function extractPlatformRequirements($links) @@ -868,10 +868,10 @@ class Installer $depPackages = $pool->whatProvides($packageName); - $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames); + $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true); + // check if the name is a glob pattern that did not match directly if (!$nameMatchesRequiredPackage) { - //maybe the name is a glob or similar that won't match directly $whitelistPatternRegexp = $this->packageNameToRegexp($packageName); foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { From bc7c93ae85ae524cee94b6dbb627eb156141515f Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 24 Feb 2014 13:34:50 +0100 Subject: [PATCH 112/638] Fix for #1966, use the preferred-install from the rootPackage config to install the dependencies. --- src/Composer/Command/CreateProjectCommand.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 5aab270ce..1b28f0e28 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -161,6 +161,24 @@ EOT $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages); } + // Update preferSource / preferDist with preferred-install from the root package if both vars still + // have their default initial value (false) + $config = $composer->getConfig(); + if ($config->has('preferred-install') && $preferDist === false && $preferSource === false) { + switch ($config->get('preferred-install')) { + case 'source': + $preferSource = true; + break; + case 'dist': + $preferDist = true; + break; + case 'auto': + default: + // noop + break; + } + } + // install dependencies of the created project if ($noInstall === false) { $installer = Installer::create($io, $composer); From 9af5eaa57461a7a6b886edaa847af36202e626f5 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 24 Feb 2014 15:27:41 +0100 Subject: [PATCH 113/638] Refactored the code with the switch statement. --- src/Composer/Command/CreateProjectCommand.php | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 1b28f0e28..bfdddf31e 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -102,18 +102,8 @@ EOT $preferSource = false; $preferDist = false; - switch ($config->get('preferred-install')) { - case 'source': - $preferSource = true; - break; - case 'dist': - $preferDist = true; - break; - case 'auto': - default: - // noop - break; - } + $this->updatePreferredOptions($config, $preferSource, $preferDist); + if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) { $preferSource = $input->getOption('prefer-source'); $preferDist = $input->getOption('prefer-dist'); @@ -143,7 +133,7 @@ EOT ); } - public function installProject(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false) + public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false) { $oldCwd = getcwd(); @@ -163,20 +153,9 @@ EOT // Update preferSource / preferDist with preferred-install from the root package if both vars still // have their default initial value (false) - $config = $composer->getConfig(); - if ($config->has('preferred-install') && $preferDist === false && $preferSource === false) { - switch ($config->get('preferred-install')) { - case 'source': - $preferSource = true; - break; - case 'dist': - $preferDist = true; - break; - case 'auto': - default: - // noop - break; - } + $rootPackageConfig = $composer->getConfig(); + if ($rootPackageConfig->has('preferred-install') && $preferDist === false && $preferSource === false) { + $this->updatePreferredOptions($rootPackageConfig, $preferSource, $preferDist); } // install dependencies of the created project @@ -256,7 +235,7 @@ EOT return 0; } - protected function installRootPackage(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false) + protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false) { if (null === $repositoryUrl) { $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); @@ -361,4 +340,28 @@ EOT { return new InstallationManager(); } + + + /** + * Updated preferSource or preferDist based on the preferredInstall config option + * @param Config $config + * @param boolean $preferSource + * @param boolean $preferDist + */ + protected function updatePreferredOptions(Config $config, &$preferSource, &$preferDist) + { + switch ($config->get('preferred-install')) { + case 'source': + $preferSource = true; + break; + case 'dist': + $preferDist = true; + break; + case 'auto': + default: + // noop + break; + } + + } } From ab8f67e8cf75dee3c784660d7467d797492bb021 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 24 Feb 2014 16:20:10 +0100 Subject: [PATCH 114/638] Always use rootPackage config --- src/Composer/Command/CreateProjectCommand.php | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index bfdddf31e..8022fb5c3 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -102,12 +102,7 @@ EOT $preferSource = false; $preferDist = false; - $this->updatePreferredOptions($config, $preferSource, $preferDist); - - if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) { - $preferSource = $input->getOption('prefer-source'); - $preferDist = $input->getOption('prefer-dist'); - } + $this->updatePreferredOptions($config, $input, $preferSource, $preferDist); if ($input->getOption('no-custom-installers')) { $output->writeln('You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.'); @@ -129,11 +124,12 @@ EOT $input->getOption('no-scripts'), $input->getOption('keep-vcs'), $input->getOption('no-progress'), - $input->getOption('no-install') + $input->getOption('no-install'), + $input ); } - public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false) + public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, InputInterface $input) { $oldCwd = getcwd(); @@ -154,9 +150,7 @@ EOT // Update preferSource / preferDist with preferred-install from the root package if both vars still // have their default initial value (false) $rootPackageConfig = $composer->getConfig(); - if ($rootPackageConfig->has('preferred-install') && $preferDist === false && $preferSource === false) { - $this->updatePreferredOptions($rootPackageConfig, $preferSource, $preferDist); - } + $this->updatePreferredOptions($rootPackageConfig, $input, $preferSource, $preferDist); // install dependencies of the created project if ($noInstall === false) { @@ -345,16 +339,19 @@ EOT /** * Updated preferSource or preferDist based on the preferredInstall config option * @param Config $config + * @param InputInterface $input * @param boolean $preferSource * @param boolean $preferDist */ - protected function updatePreferredOptions(Config $config, &$preferSource, &$preferDist) + protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist) { switch ($config->get('preferred-install')) { case 'source': $preferSource = true; + $preferDist = false; break; case 'dist': + $preferSource = false; $preferDist = true; break; case 'auto': @@ -363,5 +360,9 @@ EOT break; } + if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) { + $preferSource = $input->getOption('prefer-source'); + $preferDist = $input->getOption('prefer-dist'); + } } } From ee62ec60f0aa6f42aaac3587f46e2baf5046a036 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Mon, 24 Feb 2014 16:22:44 +0100 Subject: [PATCH 115/638] Remove old comment --- src/Composer/Command/CreateProjectCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 8022fb5c3..87369dcaf 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -147,8 +147,6 @@ EOT $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages); } - // Update preferSource / preferDist with preferred-install from the root package if both vars still - // have their default initial value (false) $rootPackageConfig = $composer->getConfig(); $this->updatePreferredOptions($rootPackageConfig, $input, $preferSource, $preferDist); From 5ed18d9aa2d08931cb9b4a61cb8beab388011e8d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 18:40:33 +0100 Subject: [PATCH 116/638] Fail over from source to dist and vice versa when downloads fail Any RuntimeException descendent will be caught and cause another download attempt using either source or dist depending on what was attempted first. --- src/Composer/Downloader/DownloadManager.php | 47 ++++++-- src/Composer/Factory.php | 2 +- tests/Composer/Test/ComposerTest.php | 3 +- .../Test/Downloader/DownloadManagerTest.php | 104 ++++++++++++++---- tests/Composer/Test/InstallerTest.php | 2 +- 5 files changed, 123 insertions(+), 35 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 51649cc3a..39a16611e 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -14,6 +14,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; use Composer\Downloader\DownloaderInterface; +use Composer\IO\IOInterface; use Composer\Util\Filesystem; /** @@ -23,6 +24,7 @@ use Composer\Util\Filesystem; */ class DownloadManager { + private $io; private $preferDist = false; private $preferSource = false; private $filesystem; @@ -31,11 +33,13 @@ class DownloadManager /** * Initializes download manager. * + * @param IOInterface $io The Input Output Interface * @param bool $preferSource prefer downloading from source * @param Filesystem|null $filesystem custom Filesystem object */ - public function __construct($preferSource = false, Filesystem $filesystem = null) + public function __construct(IOInterface $io, $preferSource = false, Filesystem $filesystem = null) { + $this->io = $io; $this->preferSource = $preferSource; $this->filesystem = $filesystem ?: new Filesystem(); } @@ -168,19 +172,44 @@ class DownloadManager $sourceType = $package->getSourceType(); $distType = $package->getDistType(); - if ((!$package->isDev() || $this->preferDist || !$sourceType) && !($preferSource && $sourceType) && $distType) { - $package->setInstallationSource('dist'); - } elseif ($sourceType) { - $package->setInstallationSource('source'); - } else { + $wantDist = !$package->isDev() || $this->preferDist || !$sourceType; + $wantSource = $preferSource && $sourceType; + + $types = array(); + if ($sourceType) { + $types[] = 'source'; + } + if ($distType) { + $types[] = 'dist'; + } + + if (empty($types)) { throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } + if ($wantDist && !$wantSource) { + $types = array_reverse($types); + } + $this->filesystem->ensureDirectoryExists($targetDir); - $downloader = $this->getDownloaderForInstalledPackage($package); - if ($downloader) { - $downloader->download($package, $targetDir); + foreach ($types as $source) { + $package->setInstallationSource($source); + try { + $downloader = $this->getDownloaderForInstalledPackage($package); + if ($downloader) { + $downloader->download($package, $targetDir); + } + break; + } catch (\RuntimeException $e) { + $this->io->write( + 'Caught an exception while trying to download '. + $package->getPrettyString(). + ': '. + $e->getMessage().'' + ); + continue; + } } } diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index f829cb459..27f83dd87 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -344,7 +344,7 @@ class Factory $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./'); } - $dm = new Downloader\DownloadManager(); + $dm = new Downloader\DownloadManager($io); switch ($config->get('preferred-install')) { case 'dist': $dm->setPreferDist(true); diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php index f667e88e5..df00c36f7 100644 --- a/tests/Composer/Test/ComposerTest.php +++ b/tests/Composer/Test/ComposerTest.php @@ -47,7 +47,8 @@ class ComposerTest extends TestCase public function testSetGetDownloadManager() { $composer = new Composer(); - $manager = $this->getMock('Composer\Downloader\DownloadManager'); + $io = $this->getMock('Composer\IO\IOInterface'); + $manager = $this->getMock('Composer\Downloader\DownloadManager', array(), array($io)); $composer->setDownloadManager($manager); $this->assertSame($manager, $composer->getDownloadManager()); diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index 48242a818..1728c583e 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -17,16 +17,18 @@ use Composer\Downloader\DownloadManager; class DownloadManagerTest extends \PHPUnit_Framework_TestCase { protected $filesystem; + protected $io; public function setUp() { $this->filesystem = $this->getMock('Composer\Util\Filesystem'); + $this->io = $this->getMock('Composer\IO\IOInterface'); } public function testSetGetDownloader() { $downloader = $this->createDownloaderMock(); - $manager = new DownloadManager(false, $this->filesystem); + $manager = new DownloadManager($this->io, false, $this->filesystem); $manager->setDownloader('test', $downloader); $this->assertSame($downloader, $manager->getDownloader('test')); @@ -43,7 +45,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getInstallationSource') ->will($this->returnValue(null)); - $manager = new DownloadManager(false, $this->filesystem); + $manager = new DownloadManager($this->io, false, $this->filesystem); $this->setExpectedException('InvalidArgumentException'); @@ -69,7 +71,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('dist')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -101,7 +103,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('source')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -135,7 +137,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('source')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -167,7 +169,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('dist')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -190,7 +192,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getType') ->will($this->returnValue('metapackage')); - $manager = new DownloadManager(false, $this->filesystem); + $manager = new DownloadManager($this->io, false, $this->filesystem); $this->assertNull($manager->getDownloaderForInstalledPackage($package)); } @@ -219,7 +221,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -231,6 +233,62 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->download($package, 'target_dir'); } + public function testFullPackageDownloadFailover() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->any()) + ->method('getPrettyString') + ->will($this->returnValue('prettyPackage')); + + $package + ->expects($this->at(3)) + ->method('setInstallationSource') + ->with('dist'); + $package + ->expects($this->at(5)) + ->method('setInstallationSource') + ->with('source'); + + $downloaderFail = $this->createDownloaderMock(); + $downloaderFail + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir') + ->will($this->throwException(new \RuntimeException("Foo"))); + + $downloaderSuccess = $this->createDownloaderMock(); + $downloaderSuccess + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->at(0)) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloaderFail)); + $manager + ->expects($this->at(1)) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloaderSuccess)); + + $manager->download($package, 'target_dir'); + } + public function testBadPackageDownload() { $package = $this->createPackageMock(); @@ -243,7 +301,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistType') ->will($this->returnValue(null)); - $manager = new DownloadManager(false, $this->filesystem); + $manager = new DownloadManager($this->io, false, $this->filesystem); $this->setExpectedException('InvalidArgumentException'); $manager->download($package, 'target_dir'); @@ -273,7 +331,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -309,7 +367,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -339,7 +397,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with('source'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -375,7 +433,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -412,7 +470,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -449,7 +507,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -474,7 +532,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistType') ->will($this->returnValue(null)); - $manager = new DownloadManager(false, $this->filesystem); + $manager = new DownloadManager($this->io, false, $this->filesystem); $manager->setPreferSource(true); $this->setExpectedException('InvalidArgumentException'); @@ -510,7 +568,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, $target, 'vendor/bundles/FOS/UserBundle'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -547,7 +605,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, 'vendor/bundles/FOS/UserBundle'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -588,7 +646,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, $target, 'vendor/pkg'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -625,7 +683,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, 'vendor/pkg'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -647,7 +705,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $target = $this->createPackageMock(); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -670,7 +728,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'vendor/bundles/FOS/UserBundle'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -687,7 +745,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $package = $this->createPackageMock(); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') - ->setConstructorArgs(array(false, $this->filesystem)) + ->setConstructorArgs(array($this->io, false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 4e992c577..cc17973bc 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -51,7 +51,7 @@ class InstallerTest extends TestCase { $io = $this->getMock('Composer\IO\IOInterface'); - $downloadManager = $this->getMock('Composer\Downloader\DownloadManager'); + $downloadManager = $this->getMock('Composer\Downloader\DownloadManager', array(), array($io)); $config = $this->getMock('Composer\Config'); $repositoryManager = new RepositoryManager($io, $config); From 35fbe3fd42b7b87e3a8b225d0631bdf64358cbf6 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 18:53:34 +0100 Subject: [PATCH 117/638] Download failover means we can now always try github zip urls for dist --- src/Composer/Repository/Vcs/GitHubDriver.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 3cf2befb5..1bcbf8a3e 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -117,10 +117,6 @@ class GitHubDriver extends VcsDriver */ public function getDist($identifier) { - if ($this->gitDriver) { - return $this->gitDriver->getDist($identifier); - } - $url = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/zipball/'.$identifier; return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => ''); From a80fde97d525ac0747f0352a507b86a0c06e4a4f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 19:22:32 +0100 Subject: [PATCH 118/638] Make the github driver behave like git if "no-api" is specified. --- doc/05-repositories.md | 5 ++++ src/Composer/Repository/Vcs/GitHubDriver.php | 26 ++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index fdb7ce1a2..a4e92d7ed 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -292,6 +292,11 @@ The VCS driver to be used is detected automatically based on the URL. However, should you need to specify one for whatever reason, you can use `git`, `svn` or `hg` as the repository type instead of `vcs`. +If you set the `no-api` key to `true` on a github repository it will clone the +repository as it would with any other git repository instead of using the +GitHub API. But unlike using the `git` driver directly, composer will still +attempt to use github's zip files. + #### Subversion Options Since Subversion has no native concept of branches and tags, Composer assumes diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 3cf2befb5..67ce43551 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -52,6 +52,11 @@ class GitHubDriver extends VcsDriver $this->originUrl = !empty($match[1]) ? $match[1] : $match[2]; $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository); + if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) { + $this->setupGitDriver(); + return; + } + $this->fetchRootIdentifier(); } @@ -405,14 +410,7 @@ class GitHubDriver extends VcsDriver // GitHub returns 404 for private repositories) and we // cannot ask for authentication credentials (because we // are not interactive) then we fallback to GitDriver. - $this->gitDriver = new GitDriver( - array('url' => $this->generateSshUrl()), - $this->io, - $this->config, - $this->process, - $this->remoteFilesystem - ); - $this->gitDriver->initialize(); + $this->setupGitDriver(); return; } catch (\RuntimeException $e) { @@ -422,4 +420,16 @@ class GitHubDriver extends VcsDriver throw $e; } } + + protected function setupGitDriver() + { + $this->gitDriver = new GitDriver( + array('url' => $this->generateSshUrl()), + $this->io, + $this->config, + $this->process, + $this->remoteFilesystem + ); + $this->gitDriver->initialize(); + } } From 1ccf4b0fc337aaecd5b34c6400c16161bfd5b849 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 19:51:03 +0100 Subject: [PATCH 119/638] Correct the tests for dist urls for github --- .../Composer/Test/Repository/Vcs/GitHubDriverTest.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index a3cb9dd23..906c20071 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -283,19 +283,16 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $this->assertEquals('test_master', $gitHubDriver->getRootIdentifier()); - // Dist is not available for GitDriver - $dist = $gitHubDriver->getDist($identifier); - $this->assertNull($dist); + $dist = $gitHubDriver->getDist($sha); + $this->assertEquals('zip', $dist['type']); + $this->assertEquals('https://api.github.com/repos/composer/packagist/zipball/SOMESHA', $dist['url']); + $this->assertEquals($sha, $dist['reference']); $source = $gitHubDriver->getSource($identifier); $this->assertEquals('git', $source['type']); $this->assertEquals($repoSshUrl, $source['url']); $this->assertEquals($identifier, $source['reference']); - // Dist is not available for GitDriver - $dist = $gitHubDriver->getDist($sha); - $this->assertNull($dist); - $source = $gitHubDriver->getSource($sha); $this->assertEquals('git', $source['type']); $this->assertEquals($repoSshUrl, $source['url']); From 31fd6c233c29cc520ead2b01a7ba4b92a601ef56 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 24 Feb 2014 19:52:20 +0100 Subject: [PATCH 120/638] Rethrow download exceptions when no options left & clean up code --- src/Composer/Downloader/DownloadManager.php | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 39a16611e..ab35d488f 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -172,28 +172,25 @@ class DownloadManager $sourceType = $package->getSourceType(); $distType = $package->getDistType(); - $wantDist = !$package->isDev() || $this->preferDist || !$sourceType; - $wantSource = $preferSource && $sourceType; - - $types = array(); + $sources = array(); if ($sourceType) { - $types[] = 'source'; + $sources[] = 'source'; } if ($distType) { - $types[] = 'dist'; + $sources[] = 'dist'; } - if (empty($types)) { + if (empty($sources)) { throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } - if ($wantDist && !$wantSource) { - $types = array_reverse($types); + if ((!$package->isDev() || $this->preferDist) && !$preferSource) { + $sources = array_reverse($sources); } $this->filesystem->ensureDirectoryExists($targetDir); - foreach ($types as $source) { + foreach ($sources as $i => $source) { $package->setInstallationSource($source); try { $downloader = $this->getDownloaderForInstalledPackage($package); @@ -202,13 +199,16 @@ class DownloadManager } break; } catch (\RuntimeException $e) { + if ($i == count($sources) - 1) { + throw $e; + } + $this->io->write( 'Caught an exception while trying to download '. $package->getPrettyString(). ': '. $e->getMessage().'' ); - continue; } } } From 665a2bd0c0d2a0a6e2f57bc9b2d518b592cf2cc5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 25 Feb 2014 13:34:39 +0100 Subject: [PATCH 121/638] Tweak error message and make TransportException extend from RuntimeException, refs #2753 --- src/Composer/Downloader/DownloadManager.php | 9 ++++++--- src/Composer/Downloader/TransportException.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index ab35d488f..f2296d7d7 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -191,6 +191,9 @@ class DownloadManager $this->filesystem->ensureDirectoryExists($targetDir); foreach ($sources as $i => $source) { + if (isset($e)) { + $this->io->write('Now trying to download from ' . $source . ''); + } $package->setInstallationSource($source); try { $downloader = $this->getDownloaderForInstalledPackage($package); @@ -204,9 +207,9 @@ class DownloadManager } $this->io->write( - 'Caught an exception while trying to download '. - $package->getPrettyString(). - ': '. + 'Failed to download '. + $package->getPrettyName(). + ' from ' . $source . ': '. $e->getMessage().'' ); } diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php index d157dde3c..b28f8d470 100644 --- a/src/Composer/Downloader/TransportException.php +++ b/src/Composer/Downloader/TransportException.php @@ -15,7 +15,7 @@ namespace Composer\Downloader; /** * @author Jordi Boggiano */ -class TransportException extends \Exception +class TransportException extends \RuntimeException { protected $headers; From edfaf727e57b1616579c99a26b4f2edaa3ff0d5a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 25 Feb 2014 15:55:44 +0100 Subject: [PATCH 122/638] When using the github driver with no-api don't reset to an ssh url --- src/Composer/Repository/Vcs/GitHubDriver.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 60f343ab2..c99c8cf18 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -53,7 +53,7 @@ class GitHubDriver extends VcsDriver $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository); if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) { - $this->setupGitDriver(); + $this->setupGitDriver($this->getUrl()); return; } @@ -406,7 +406,7 @@ class GitHubDriver extends VcsDriver // GitHub returns 404 for private repositories) and we // cannot ask for authentication credentials (because we // are not interactive) then we fallback to GitDriver. - $this->setupGitDriver(); + $this->setupGitDriver($this->generateSshUrl()); return; } catch (\RuntimeException $e) { @@ -417,10 +417,10 @@ class GitHubDriver extends VcsDriver } } - protected function setupGitDriver() + protected function setupGitDriver($url) { $this->gitDriver = new GitDriver( - array('url' => $this->generateSshUrl()), + array('url' => $url), $this->io, $this->config, $this->process, From b808ff5e28944ab2e25e050f8df848c5842bc14c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 25 Feb 2014 15:57:35 +0100 Subject: [PATCH 123/638] Don't hardcode the URL to an https one either --- src/Composer/Repository/Vcs/GitHubDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index c99c8cf18..38c7869a5 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -53,7 +53,7 @@ class GitHubDriver extends VcsDriver $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository); if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) { - $this->setupGitDriver($this->getUrl()); + $this->setupGitDriver($this->url); return; } From e1e48b28f75e9c6a293fc032588093c6f6d8d3fb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 10:43:26 +0100 Subject: [PATCH 124/638] Update vendor dir modified time after every install/update, fixes #2764 --- src/Composer/Installer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 4e4502dfe..d373beb43 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -297,6 +297,11 @@ class Installer $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; $this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode); } + + $vendorDir = $this->config->get('vendor-dir'); + if (is_dir($vendorDir)) { + touch($vendorDir); + } } return 0; From 3811fe7d8b30c21ffcb401eb1bc9e1d2749cb794 Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Wed, 26 Feb 2014 21:23:15 +1100 Subject: [PATCH 125/638] Resolves #2521. First and simple attempt to make a gzip downloader --- src/Composer/Downloader/GzipDownloader.php | 53 ++++++++++++++++++++++ src/Composer/Factory.php | 1 + 2 files changed, 54 insertions(+) create mode 100644 src/Composer/Downloader/GzipDownloader.php diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php new file mode 100644 index 000000000..6736c9cc1 --- /dev/null +++ b/src/Composer/Downloader/GzipDownloader.php @@ -0,0 +1,53 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +use Composer\Config; +use Composer\Cache; +use Composer\EventDispatcher\EventDispatcher; +use Composer\Util\ProcessExecutor; +use Composer\IO\IOInterface; + +/** + * GZip archive downloader. + * + * @author Pavel Puchkin + */ +class GzipDownloader extends ArchiveDownloader +{ + protected $process; + + public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null) + { + $this->process = $process ?: new ProcessExecutor($io); + parent::__construct($io, $config, $eventDispatcher, $cache); + } + + protected function extract($file, $path) + { + $processError = null; + + // Try to use gunzip on *nix + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $targetDirectory = $path . DIRECTORY_SEPARATOR . basename($file); + $command = 'gzip -d < ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetDirectory); + + if (0 === $this->process->execute($command, $ignoredOutput)) { + return; + } + + $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + } + } +} + diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 27f83dd87..d0cd68b79 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -365,6 +365,7 @@ class Factory $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache)); + $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache)); From a2878846195dd0eba742099fabc2a1bda8e10a71 Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Wed, 26 Feb 2014 22:52:47 +1100 Subject: [PATCH 126/638] There is no need in DIRECTORY_SEPARATOR since it Unix --- src/Composer/Downloader/GzipDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 6736c9cc1..e14b7de02 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -39,7 +39,7 @@ class GzipDownloader extends ArchiveDownloader // Try to use gunzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $targetDirectory = $path . DIRECTORY_SEPARATOR . basename($file); + $targetDirectory = $path . '/' . basename($file); $command = 'gzip -d < ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetDirectory); if (0 === $this->process->execute($command, $ignoredOutput)) { From ba56ac362a8ee0a953c8340410723568a4a282b6 Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Wed, 26 Feb 2014 23:49:53 +1100 Subject: [PATCH 127/638] Final fix. Preserve initial file name --- src/Composer/Downloader/GzipDownloader.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index e14b7de02..11e1b8ba6 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -15,6 +15,7 @@ namespace Composer\Downloader; use Composer\Config; use Composer\Cache; use Composer\EventDispatcher\EventDispatcher; +use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; @@ -39,15 +40,24 @@ class GzipDownloader extends ArchiveDownloader // Try to use gunzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $targetDirectory = $path . '/' . basename($file); - $command = 'gzip -d < ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetDirectory); + $targetFile = $path . '/' . basename(substr($file, 0, -3)); + $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFile); if (0 === $this->process->execute($command, $ignoredOutput)) { return; } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + throw new \RuntimeException($processError); } } + + /** + * {@inheritdoc} + */ + protected function getFileName(PackageInterface $package, $path) + { + return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME); + } } From c598fdb0f50542c6963ae01839bb6f8c04e164e2 Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Thu, 27 Feb 2014 00:01:11 +1100 Subject: [PATCH 128/638] Since there is no solution for non-unix (for now), remove the condition --- src/Composer/Downloader/GzipDownloader.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 11e1b8ba6..61f92caf9 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -36,20 +36,15 @@ class GzipDownloader extends ArchiveDownloader protected function extract($file, $path) { - $processError = null; + $targetFile = $path . '/' . basename(substr($file, 0, -3)); + $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFile); - // Try to use gunzip on *nix - if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $targetFile = $path . '/' . basename(substr($file, 0, -3)); - $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFile); - - if (0 === $this->process->execute($command, $ignoredOutput)) { - return; - } - - $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); - throw new \RuntimeException($processError); + if (0 === $this->process->execute($command, $ignoredOutput)) { + return; } + + $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + throw new \RuntimeException($processError); } /** From 28bb78132445cbccbffac373a3bfa3b974eb876f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 15:51:06 +0100 Subject: [PATCH 129/638] Capture response bodies in exceptions when http requests fail --- .../Downloader/TransportException.php | 11 +++++++++ src/Composer/Util/RemoteFilesystem.php | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php index b28f8d470..2e4b42f01 100644 --- a/src/Composer/Downloader/TransportException.php +++ b/src/Composer/Downloader/TransportException.php @@ -18,6 +18,7 @@ namespace Composer\Downloader; class TransportException extends \RuntimeException { protected $headers; + protected $response; public function setHeaders($headers) { @@ -28,4 +29,14 @@ class TransportException extends \RuntimeException { return $this->headers; } + + public function setResponse($response) + { + $this->response = $response; + } + + public function getResponse() + { + return $this->response; + } } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index d3ecec03d..68c76f341 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -124,6 +124,9 @@ class RemoteFilesystem $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token']; unset($options['github-token']); } + if (isset($options['http'])) { + $options['http']['ignore_errors'] = true; + } $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet'))); if ($this->progress) { @@ -145,6 +148,10 @@ class RemoteFilesystem if ($e instanceof TransportException && !empty($http_response_header[0])) { $e->setHeaders($http_response_header); } + if ($e instanceof TransportException && $result !== false) { + $e->setResponse($result); + } + $result = false; } if ($errorMessage && !ini_get('allow_url_fopen')) { $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')'; @@ -154,10 +161,16 @@ class RemoteFilesystem throw $e; } - // fix for 5.4.0 https://bugs.php.net/bug.php?id=61336 + // fail 4xx and 5xx responses and capture the response if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ ([45]\d\d)}i', $http_response_header[0], $match)) { - $result = false; $errorCode = $match[1]; + if (!$this->retry) { + $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $errorCode); + $e->setHeaders($http_response_header); + $e->setResponse($result); + throw $e; + } + $result = false; } // decode gzip @@ -250,12 +263,7 @@ class RemoteFilesystem $this->promptAuthAndRetry(); break; } - - if ($notificationCode === STREAM_NOTIFY_AUTH_REQUIRED) { - break; - } - - throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', $messageCode); + break; case STREAM_NOTIFY_AUTH_RESULT: if (403 === $messageCode) { From 5067d76dbc60f26992da4402c3f21af91bd4b57a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 16:01:31 +0100 Subject: [PATCH 130/638] Adjust test suite --- tests/Composer/Test/Util/RemoteFilesystemTest.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index eabfe9ed5..92aa60630 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -130,18 +130,11 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase $this->assertAttributeEquals(50, 'lastProgress', $fs); } - public function testCallbackGetNotifyFailure404() + public function testCallbackGetPassesThrough404() { $fs = new RemoteFilesystem($this->getMock('Composer\IO\IOInterface')); - try { - $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0); - $this->fail(); - } catch (\Exception $e) { - $this->assertInstanceOf('Composer\Downloader\TransportException', $e); - $this->assertEquals(404, $e->getCode()); - $this->assertContains('HTTP/1.1 404 Not Found', $e->getMessage()); - } + $this->assertNull($this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0)); } public function testCaptureAuthenticationParamsFromUrl() From 1851c29dd3d2d05096d3e1d2166fc0d73aa622eb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 17:19:54 +0100 Subject: [PATCH 131/638] Update code to work with #2766 --- src/Composer/Util/RemoteFilesystem.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 7c7957ca7..fd5d431c6 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -262,17 +262,17 @@ class RemoteFilesystem case STREAM_NOTIFY_FAILURE: case STREAM_NOTIFY_AUTH_REQUIRED: if (401 === $messageCode) { + // Bail if the caller is going to handle authentication failures itself. + if (!$this->retryAuthFailure) { + break; + } + if (!$this->io->isInteractive()) { $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; throw new TransportException($message, 401); } - // Bail if the caller is going to handle authentication failures itself. - if (!$this->retryAuthFailure) { - throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', 401); - } - $this->promptAuthAndRetry(); break; } From 5b0dc99fec6d2233db57d97cac3dd15824ea21f7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 17:20:47 +0100 Subject: [PATCH 132/638] Reuse github existing tokens instead of failing, fixes #2724 --- src/Composer/Util/GitHub.php | 50 +++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 942e7749c..b69073d13 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -101,25 +101,51 @@ class GitHub $appName .= ' on ' . trim($output); } - $headers = array('Content-Type: application/json'); - + $headers = array(); if ($otp) { - $headers[] = 'X-GitHub-OTP: ' . $otp; + $headers = array('X-GitHub-OTP: ' . $otp); } - $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( + // try retrieving an existing token with the same name + $contents = null; + $auths = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( 'retry-auth-failure' => false, 'http' => array( - 'method' => 'POST', - 'follow_location' => false, - 'header' => $headers, - 'content' => json_encode(array( - 'scopes' => array('repo'), - 'note' => $appName, - 'note_url' => 'https://getcomposer.org/', - )), + 'header' => $headers ) ))); + foreach ($auths as $auth) { + if ( + isset($auth['app']['name']) + && 0 === strpos($auth['app']['name'], $appName) + && $auth['app']['url'] === 'https://getcomposer.org/' + ) { + $this->io->write('An existing OAuth token for Composer is present and will be reused'); + + $contents['token'] = $auth['token']; + break; + } + } + + // no existing token, create one + if (empty($contents['token'])) { + $headers[] = array('Content-Type: application/json'); + + $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'follow_location' => false, + 'header' => $headers, + 'content' => json_encode(array( + 'scopes' => array('repo'), + 'note' => $appName, + 'note_url' => 'https://getcomposer.org/', + )), + ) + ))); + $this->io->write('Token successfully created'); + } } catch (TransportException $e) { if (in_array($e->getCode(), array(403, 401))) { // 401 when authentication was supplied, handle 2FA if required. From 0d4c2bb7d7a864a9b3e876908e743310cdeaa5e6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 26 Feb 2014 17:38:58 +0100 Subject: [PATCH 133/638] Fix github test --- tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index 906c20071..f452f224d 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -81,9 +81,14 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $remoteFilesystem->expects($this->at(1)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/authorizations'), $this->equalTo(false)) - ->will($this->returnValue('{"token": "abcdef"}')); + ->will($this->returnValue('[]')); $remoteFilesystem->expects($this->at(2)) + ->method('getContents') + ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/authorizations'), $this->equalTo(false)) + ->will($this->returnValue('{"token": "abcdef"}')); + + $remoteFilesystem->expects($this->at(3)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master", "private": true}')); From 21109ada221ffe993c1ce294bdf1c858f76a983d Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Thu, 27 Feb 2014 15:17:15 +0100 Subject: [PATCH 134/638] Fix for #2739 (and #1755), added support for directory excludes in the .gitignore file like /directory or directory/ --- .../Package/Archiver/BaseExcludeFilter.php | 4 +-- .../Archiver/ArchivableFilesFinderTest.php | 35 +++++++++++++++++++ .../Package/Archiver/GitExcludeFilterTest.php | 4 +-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index f8c390e41..032a32d35 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -140,8 +140,8 @@ abstract class BaseExcludeFilter $pattern .= '/'; } - // remove delimiters as well as caret (^) from the regex - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -1); + // remove delimiters as well as caret (^) and dollar sign ($) from the regex + $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '/*.*$'; return array($pattern . '#', $negate, false); } diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php index 536f2128c..7200a699f 100644 --- a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php @@ -46,6 +46,24 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'B/sub/prefixD.foo', 'B/sub/prefixE.foo', 'B/sub/prefixF.foo', + 'C/prefixA.foo', + 'C/prefixB.foo', + 'C/prefixC.foo', + 'C/prefixD.foo', + 'C/prefixE.foo', + 'C/prefixF.foo', + 'D/prefixA', + 'D/prefixB', + 'D/prefixC', + 'D/prefixD', + 'D/prefixE', + 'D/prefixF', + 'E/prefixA.foo', + 'E/prefixB.foo', + 'E/prefixC.foo', + 'E/prefixD.foo', + 'E/prefixE.foo', + 'E/prefixF.foo', 'toplevelA.foo', 'toplevelB.foo', 'prefixA.foo', @@ -91,6 +109,20 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase '/B/sub/prefixD.foo', '/B/sub/prefixE.foo', '/B/sub/prefixF.foo', + '/C/prefixA.foo', + '/C/prefixD.foo', + '/C/prefixE.foo', + '/C/prefixF.foo', + '/D/prefixA', + '/D/prefixB', + '/D/prefixC', + '/D/prefixD', + '/D/prefixE', + '/D/prefixF', + '/E/prefixA.foo', + '/E/prefixD.foo', + '/E/prefixE.foo', + '/E/prefixF.foo', '/prefixB.foo', '/prefixD.foo', '/prefixE.foo', @@ -120,6 +152,9 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase '!/*/*/prefixF.foo', '', 'refixD.foo', + '/C', + 'D/prefixA', + 'E/' ))); // git does not currently support negative git attributes diff --git a/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php index a6d473a1e..e2eef1722 100644 --- a/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php +++ b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php @@ -29,8 +29,8 @@ class GitExcludeFilterTest extends \PHPUnit_Framework_TestCase public function patterns() { return array( - array('app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml$#', false, false)), - array('!app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml$#', true, false)), + array('app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml/*.*$#', false, false)), + array('!app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml/*.*$#', true, false)), ); } } From db91454a13928c8d7efde0caf3f899a3c1a775e7 Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Thu, 27 Feb 2014 10:39:33 +0100 Subject: [PATCH 135/638] added an autoload-dev section --- composer.json | 3 + doc/04-schema.md | 21 ++++++ res/composer-schema.json | 24 +++++++ src/Composer/Autoload/AutoloadGenerator.php | 10 +++ src/Composer/Command/DumpAutoloadCommand.php | 5 +- src/Composer/Installer.php | 1 + src/Composer/Package/AliasPackage.php | 4 ++ src/Composer/Package/Dumper/ArrayDumper.php | 1 + src/Composer/Package/Loader/ArrayLoader.php | 4 ++ src/Composer/Package/Package.php | 19 ++++++ src/Composer/Package/PackageInterface.php | 16 ++++- .../Test/Autoload/AutoloadGeneratorTest.php | 68 +++++++++++++++++++ .../Autoload/Fixtures/autoload_classmap7.php | 10 +++ .../Autoload/Fixtures/autoload_files2.php | 10 +++ .../Test/Autoload/Fixtures/autoload_main4.php | 10 +++ tests/bootstrap.php | 2 + 16 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/autoload_files2.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/autoload_main4.php diff --git a/composer.json b/composer.json index b72856837..ea526e9c7 100644 --- a/composer.json +++ b/composer.json @@ -39,6 +39,9 @@ "autoload": { "psr-0": { "Composer": "src/" } }, + "autoload-dev": { + "psr-0": { "Composer\\Test": "tests/" } + }, "bin": ["bin/composer"], "extra": { "branch-alias": { diff --git a/doc/04-schema.md b/doc/04-schema.md index 6ebe378e4..ae6894f09 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -516,6 +516,27 @@ Example: } } +### autoload-dev (root-only) + +This section allows to define autoload rules for development purpose. + +If you're generating classmaps from your PSR-0 namespaces, you're probably concerned +about performance, if so, you'll also don't want your test classes to be mixed up +with your regular classes in those classmaps. + +Therefore, it is a good idea to rely on a dedicated path for your unit tests. + +Example: + + { + "autoload": { + "psr-0": { "MyLibrary": "src/" } + }, + "autoload-dev": { + "psr-0": { "MyLibrary\\Tests": "tests/" } + } + } + ### include-path > **DEPRECATED**: This is only present to support legacy projects, and all new code diff --git a/res/composer-schema.json b/res/composer-schema.json index 905199247..69d4dc5a1 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -226,6 +226,30 @@ } } }, + "autoload-dev": { + "type": "object", + "description": "Description of additional autoload rules for development purpose (eg. a test suite).", + "properties": { + "psr-0": { + "type": "object", + "description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.", + "additionalProperties": true + }, + "psr-4": { + "type": "object", + "description": "This is a hash of namespaces (keys) and the PSR-4 directories they can map to (values, can be arrays of paths) by the autoloader.", + "additionalProperties": true + }, + "classmap": { + "type": "array", + "description": "This is an array of directories that contain classes to be included in the class-map generation process." + }, + "files": { + "type": "array", + "description": "This is an array of files that are always required on every request." + } + } + }, "archive": { "type": ["object"], "description": "Options for creating package archives for distribution.", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c4c172898..8b4d0443f 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -32,11 +32,18 @@ class AutoloadGenerator */ private $eventDispatcher; + private $devMode = false; + public function __construct(EventDispatcher $eventDispatcher) { $this->eventDispatcher = $eventDispatcher; } + public function setDevMode($devMode = true) + { + $this->devMode = (boolean) $devMode; + } + public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') { $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP); @@ -569,6 +576,9 @@ FOOTER; list($package, $installPath) = $item; $autoload = $package->getAutoload(); + if ($this->devMode && $package === $mainPackage) { + $autoload = array_merge_recursive($autoload, $package->getDevAutoload()); + } // skip misconfigured packages if (!isset($autoload[$type]) || !is_array($autoload[$type])) { diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index d228fb150..cd3f1b71d 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -31,6 +31,7 @@ class DumpAutoloadCommand extends Command ->setDescription('Dumps the autoloader') ->setDefinition(array( new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 packages to be loaded with classmaps too, good for production.'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables dev autoload.'), )) ->setHelp(<<php composer.phar dump-autoload @@ -59,6 +60,8 @@ EOT $output->writeln('Generating autoload files'); } - $composer->getAutoloadGenerator()->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); + $generator = $composer->getAutoloadGenerator(); + $generator->setDevMode($input->getOption('dev')); + $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index d373beb43..839fc45e4 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -290,6 +290,7 @@ class Installer $this->io->write('Generating autoload files'); } + $this->autoloadGenerator->setDevMode($this->devMode); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); if ($this->runScripts) { diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 6f1fb5095..631b035a7 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -245,6 +245,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getAutoload(); } + public function getDevAutoload() + { + return $this->aliasOf->getDevAutoload(); + } public function getIncludePaths() { return $this->aliasOf->getIncludePaths(); diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index be94adec4..0a183742f 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -31,6 +31,7 @@ class ArrayDumper 'extra', 'installationSource' => 'installation-source', 'autoload', + 'devAutoload' => 'autoload-dev', 'notificationUrl' => 'notification-url', 'includePaths' => 'include-path', ); diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 3940bdeb0..6ef4e7c03 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -130,6 +130,10 @@ class ArrayLoader implements LoaderInterface $package->setAutoload($config['autoload']); } + if (isset($config['autoload-dev'])) { + $package->setDevAutoload($config['autoload-dev']); + } + if (isset($config['include-path'])) { $package->setIncludePaths($config['include-path']); } diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index ba3f611c1..8fab59a8a 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -47,6 +47,7 @@ class Package extends BasePackage protected $devRequires = array(); protected $suggests = array(); protected $autoload = array(); + protected $devAutoload = array(); protected $includePaths = array(); protected $archiveExcludes = array(); @@ -440,6 +441,24 @@ class Package extends BasePackage return $this->autoload; } + /** + * Set the dev autoload mapping + * + * @param array $autoload Mapping of dev autoloading rules + */ + public function setDevAutoload(array $devAutoload) + { + $this->devAutoload = $devAutoload; + } + + /** + * {@inheritDoc} + */ + public function getDevAutoload() + { + return $this->devAutoload; + } + /** * Sets the list of paths added to PHP's include path. * diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index a3c8a2793..fd7393992 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -231,13 +231,25 @@ interface PackageInterface * * {"": {""}} * - * Type is either "psr-0" or "pear". Namespaces are mapped to directories - * for autoloading using the type specified. + * Type is either "psr-4", "psr-0", "classmap" or "files". Namespaces are mapped to + * directories for autoloading using the type specified. * * @return array Mapping of autoloading rules */ public function getAutoload(); + /** + * Returns an associative array of dev autoloading rules + * + * {"": {""}} + * + * Type is either "psr-4", "psr-0", "classmap" or "files". Namespaces are mapped to + * directories for autoloading using the type specified. + * + * @return array Mapping of dev autoloading rules + */ + public function getDevAutoload(); + /** * Returns a list of directories which should get added to PHP's * include path. diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 9f1e2fb16..1bbf0b577 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -170,7 +170,75 @@ class AutoloadGeneratorTest extends TestCase // Assert that autoload_classmap.php was correctly generated. $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } + + public function testMainPackageDevAutoloading() + { + $package = new Package('a', '1.0', '1.0'); + $package->setAutoload(array( + 'psr-0' => array( + 'Main' => 'src/', + ), + )); + $package->setDevAutoload(array( + 'files' => array('devfiles/foo.php'), + )); + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array())); + + $this->fs->ensureDirectoryExists($this->workingDir.'/composer'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src/Main'); + file_put_contents($this->workingDir.'/src/Main/ClassMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/devfiles'); + file_put_contents($this->workingDir.'/devfiles/foo.php', 'generator->setDevMode(true); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + + // check standard autoload + $this->assertAutoloadFiles('main4', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('classmap7', $this->vendorDir.'/composer', 'classmap'); + + // make sure dev autoload is correctly dumped + $this->assertAutoloadFiles('files2', $this->vendorDir.'/composer', 'files'); + } + + public function testMainPackageDevAutoloadingDisabledByDefault() + { + $package = new Package('a', '1.0', '1.0'); + $package->setAutoload(array( + 'psr-0' => array( + 'Main' => 'src/', + ), + )); + $package->setDevAutoload(array( + 'files' => array('devfiles/foo.php'), + )); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array())); + + $this->fs->ensureDirectoryExists($this->workingDir.'/composer'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src/Main'); + file_put_contents($this->workingDir.'/src/Main/ClassMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/devfiles'); + file_put_contents($this->workingDir.'/devfiles/foo.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + + // check standard autoload + $this->assertAutoloadFiles('main4', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('classmap7', $this->vendorDir.'/composer', 'classmap'); + + // make sure dev autoload is disabled when dev mode is set to false + $this->assertFalse(is_file($this->vendorDir.'/composer/autoload_files.php')); + } + public function testVendorDirSameAsWorkingDir() { $this->vendorDir = $this->workingDir; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php new file mode 100644 index 000000000..5768726d1 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php @@ -0,0 +1,10 @@ + $baseDir . '/src/Main/ClassMain.php', +); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php new file mode 100644 index 000000000..13cb9ecb3 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php @@ -0,0 +1,10 @@ + array($baseDir . '/src'), +); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c5e16d625..12caaffac 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -13,6 +13,8 @@ error_reporting(E_ALL); $loader = require __DIR__.'/../src/bootstrap.php'; + +// to be removed $loader->add('Composer\Test', __DIR__); require __DIR__.'/Composer/TestCase.php'; From 7ccb91667f8c4c3f95d8705f5986f86f5a2e350e Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Fri, 28 Feb 2014 10:30:12 +1100 Subject: [PATCH 136/638] Fallback to gzip functions when on Windows --- src/Composer/Downloader/GzipDownloader.php | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 61f92caf9..4444db369 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -36,15 +36,28 @@ class GzipDownloader extends ArchiveDownloader protected function extract($file, $path) { - $targetFile = $path . '/' . basename(substr($file, 0, -3)); - $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFile); + $targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3)); - if (0 === $this->process->execute($command, $ignoredOutput)) { - return; + // Try to use gunzip on *nix + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFilepath); + + if (0 === $this->process->execute($command, $ignoredOutput)) { + return; + } + + $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + throw new \RuntimeException($processError); } - $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); - throw new \RuntimeException($processError); + // Gzip 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); } /** From 1d51e54a31dfe0225b3ebc5aaac8618555ec5856 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Fri, 28 Feb 2014 11:43:28 +0100 Subject: [PATCH 137/638] Updated the regexp and added more test cases. --- .../Package/Archiver/BaseExcludeFilter.php | 7 ++- .../Archiver/ArchivableFilesFinderTest.php | 43 +++++++++++++------ .../Package/Archiver/GitExcludeFilterTest.php | 4 +- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index 032a32d35..a62d56596 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -136,12 +136,15 @@ abstract class BaseExcludeFilter if (strlen($rule) && $rule[0] === '/') { $pattern .= '^/'; $rule = substr($rule, 1); - } elseif (false === strpos($rule, '/') || strlen($rule) - 1 === strpos($rule, '/')) { + } elseif (strlen($rule) - 1 === strpos($rule, '/')) { + $pattern .= '^/'; + $rule = substr($rule, 0, -1); + } elseif (false === strpos($rule, '/')) { $pattern .= '/'; } // remove delimiters as well as caret (^) and dollar sign ($) from the regex - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '/*.*$'; + $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '(?=$|/)'; return array($pattern . '#', $negate, false); } diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php index 7200a699f..ed87ee2f0 100644 --- a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php @@ -58,12 +58,10 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'D/prefixD', 'D/prefixE', 'D/prefixF', - 'E/prefixA.foo', - 'E/prefixB.foo', - 'E/prefixC.foo', - 'E/prefixD.foo', - 'E/prefixE.foo', - 'E/prefixF.foo', + '/E/subtestA.foo', + '/F/subtestA.foo', + '/G/subtestA.foo', + '/H/subtestA.foo', 'toplevelA.foo', 'toplevelB.foo', 'prefixA.foo', @@ -72,6 +70,10 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'prefixD.foo', 'prefixE.foo', 'prefixF.foo', + 'parameters.yml', + 'parameters.yml.dist', + '!important!.txt', + '!important_too!.txt' ); foreach ($fileTree as $relativePath) { @@ -100,6 +102,8 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase $this->finder = new ArchivableFilesFinder($this->sources, $excludes); $this->assertArchivableFiles(array( + '/!important!.txt', + '/!important_too!.txt', '/A/prefixA.foo', '/A/prefixD.foo', '/A/prefixE.foo', @@ -119,10 +123,12 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase '/D/prefixD', '/D/prefixE', '/D/prefixF', - '/E/prefixA.foo', - '/E/prefixD.foo', - '/E/prefixE.foo', - '/E/prefixF.foo', + '/E/subtestA.foo', + '/F/subtestA.foo', + '/G/subtestA.foo', + '/H/subtestA.foo', + '/parameters.yml', + '/parameters.yml.dist', '/prefixB.foo', '/prefixD.foo', '/prefixE.foo', @@ -154,7 +160,12 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'refixD.foo', '/C', 'D/prefixA', - 'E/' + 'E', + 'F/', + 'G/*', + 'H/**', + 'parameters.yml', + '\!important!.txt' ))); // git does not currently support negative git attributes @@ -195,9 +206,15 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase '# comments', '', '^prefixD.foo', + 'D/prefixA', + 'parameters.yml', + '\!important!.txt', + 'E', + 'F/', 'syntax: glob', 'prefixF.*', 'B/*', + 'H/**', ))); $this->finder = new ArchivableFilesFinder($this->sources, array()); @@ -208,7 +225,9 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'hg archive archive.zip' ); - array_shift($expectedFiles); // remove .hg_archival.txt + // Remove .hg_archival.txt from the expectedFiles + $archiveKey = array_search('/.hg_archival.txt', $expectedFiles); + array_splice($expectedFiles, $archiveKey, 1); $this->assertArchivableFiles($expectedFiles); } diff --git a/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php index e2eef1722..97c02c8e6 100644 --- a/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php +++ b/tests/Composer/Test/Package/Archiver/GitExcludeFilterTest.php @@ -29,8 +29,8 @@ class GitExcludeFilterTest extends \PHPUnit_Framework_TestCase public function patterns() { return array( - array('app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml/*.*$#', false, false)), - array('!app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml/*.*$#', true, false)), + array('app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml(?=$|/)#', false, false)), + array('!app/config/parameters.yml', array('#(?=[^\.])app/(?=[^\.])config/(?=[^\.])parameters\.yml(?=$|/)#', true, false)), ); } } From 3e161e8ea988543ab7ba3eb35dca8101f9f525ef Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Fri, 28 Feb 2014 16:30:55 +0100 Subject: [PATCH 138/638] Updated the regexp and added an other test case for sub dirs --- src/Composer/Package/Archiver/BaseExcludeFilter.php | 2 +- .../Package/Archiver/ArchivableFilesFinderTest.php | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index a62d56596..dca24eb67 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -137,7 +137,7 @@ abstract class BaseExcludeFilter $pattern .= '^/'; $rule = substr($rule, 1); } elseif (strlen($rule) - 1 === strpos($rule, '/')) { - $pattern .= '^/'; + $pattern .= '/'; $rule = substr($rule, 0, -1); } elseif (false === strpos($rule, '/')) { $pattern .= '/'; diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php index ed87ee2f0..bc74be1e9 100644 --- a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php @@ -58,10 +58,12 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'D/prefixD', 'D/prefixE', 'D/prefixF', - '/E/subtestA.foo', - '/F/subtestA.foo', - '/G/subtestA.foo', - '/H/subtestA.foo', + 'E/subtestA.foo', + 'F/subtestA.foo', + 'G/subtestA.foo', + 'H/subtestA.foo', + 'I/J/subtestA.foo', + 'K/dirJ/subtestA.foo', 'toplevelA.foo', 'toplevelB.foo', 'prefixA.foo', @@ -127,6 +129,8 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase '/F/subtestA.foo', '/G/subtestA.foo', '/H/subtestA.foo', + '/I/J/subtestA.foo', + '/K/dirJ/subtestA.foo', '/parameters.yml', '/parameters.yml.dist', '/prefixB.foo', @@ -164,6 +168,7 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase 'F/', 'G/*', 'H/**', + 'J/', 'parameters.yml', '\!important!.txt' ))); From cea7c07cf272f3abea8e3aeb24dfe03b44714f2f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 1 Mar 2014 19:58:37 +0100 Subject: [PATCH 139/638] Fix CS --- src/Composer/Autoload/AutoloadGenerator.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index ecb5a4c70..4fc900187 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -578,15 +578,13 @@ FOOTER; foreach ($autoload[$type] as $namespace => $paths) { foreach ((array) $paths as $path) { - if (($type === 'files' || $type === 'classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) - { + if (($type === 'files' || $type === 'classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { // remove target-dir from file paths of the root package if ($package === $mainPackage) { $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); - } - // add target-dir from file paths that don't have it - else { + } else { + // add target-dir from file paths that don't have it $path = $package->getTargetDir() . '/' . $path; } } From 790a25c348fcb4b937f7c35c2b61dc82905252ac Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 1 Mar 2014 20:39:06 +0100 Subject: [PATCH 140/638] Adjust dump command, add another test, update docs, refs #1344 --- .travis.yml | 4 ++-- doc/03-cli.md | 1 + doc/04-schema.md | 15 ++++++++------- src/Composer/Command/DumpAutoloadCommand.php | 4 ++-- .../Test/Autoload/AutoloadGeneratorTest.php | 13 ++++++++----- .../Test/Autoload/Fixtures/autoload_main5.php | 10 ++++++++++ tests/bootstrap.php | 6 +----- 7 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/autoload_main5.php diff --git a/.travis.yml b/.travis.yml index 576b2bcbd..a2803f0e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ matrix: before_script: - sudo apt-get install parallel - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - - composer install --dev --prefer-source - - bin/composer install --dev --prefer-source + - composer install --prefer-source + - bin/composer install --prefer-source - git config --global user.name travis-ci - git config --global user.email travis@example.com diff --git a/doc/03-cli.md b/doc/03-cli.md index 3e9ae78e2..e885a7a9f 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -387,6 +387,7 @@ performance. * **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. +* **--no-dev:** Disables autoload-dev rules. ## licenses diff --git a/doc/04-schema.md b/doc/04-schema.md index ad8b53623..071b3bd9f 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -518,22 +518,23 @@ Example: ### autoload-dev (root-only) -This section allows to define autoload rules for development purpose. +This section allows to define autoload rules for development purposes. -If you're generating classmaps from your PSR-0 namespaces, you're probably concerned -about performance, if so, you'll also don't want your test classes to be mixed up -with your regular classes in those classmaps. +Classes needed to run the test suite should not be included in the main autoload +rules to avoid polluting the autoloader in production and when other people use +your package as a dependency. -Therefore, it is a good idea to rely on a dedicated path for your unit tests. +Therefore, it is a good idea to rely on a dedicated path for your unit tests +and to add it within the autoload-dev section. Example: { "autoload": { - "psr-0": { "MyLibrary": "src/" } + "psr-4": { "MyLibrary\\": "src/" } }, "autoload-dev": { - "psr-0": { "MyLibrary\\Tests": "tests/" } + "psr-4": { "MyLibrary\\Tests": "tests/" } } } diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index cd3f1b71d..86d351505 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -31,7 +31,7 @@ class DumpAutoloadCommand extends Command ->setDescription('Dumps the autoloader') ->setDefinition(array( new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 packages to be loaded with classmaps too, good for production.'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables dev autoload.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), )) ->setHelp(<<php composer.phar dump-autoload @@ -61,7 +61,7 @@ EOT } $generator = $composer->getAutoloadGenerator(); - $generator->setDevMode($input->getOption('dev')); + $generator->setDevMode(!$input->getOption('no-dev')); $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); } } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 1bbf0b577..7c4ddccd9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -170,7 +170,7 @@ class AutoloadGeneratorTest extends TestCase // Assert that autoload_classmap.php was correctly generated. $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } - + public function testMainPackageDevAutoloading() { $package = new Package('a', '1.0', '1.0'); @@ -181,6 +181,9 @@ class AutoloadGeneratorTest extends TestCase )); $package->setDevAutoload(array( 'files' => array('devfiles/foo.php'), + 'psr-0' => array( + 'Main' => 'tests/' + ), )); $this->repository->expects($this->once()) @@ -197,11 +200,11 @@ class AutoloadGeneratorTest extends TestCase // generate autoload files with the dev mode set to true $this->generator->setDevMode(true); $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); - + // check standard autoload - $this->assertAutoloadFiles('main4', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('main5', $this->vendorDir.'/composer'); $this->assertAutoloadFiles('classmap7', $this->vendorDir.'/composer', 'classmap'); - + // make sure dev autoload is correctly dumped $this->assertAutoloadFiles('files2', $this->vendorDir.'/composer', 'files'); } @@ -238,7 +241,7 @@ class AutoloadGeneratorTest extends TestCase // make sure dev autoload is disabled when dev mode is set to false $this->assertFalse(is_file($this->vendorDir.'/composer/autoload_files.php')); } - + public function testVendorDirSameAsWorkingDir() { $this->vendorDir = $this->workingDir; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main5.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main5.php new file mode 100644 index 000000000..15cb2622b --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main5.php @@ -0,0 +1,10 @@ + array($baseDir . '/src', $baseDir . '/tests'), +); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 12caaffac..9e59c65a7 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,9 +12,5 @@ error_reporting(E_ALL); -$loader = require __DIR__.'/../src/bootstrap.php'; - -// to be removed -$loader->add('Composer\Test', __DIR__); - +require __DIR__.'/../src/bootstrap.php'; require __DIR__.'/Composer/TestCase.php'; From 5fb005631a7de07db83ae8af894b7a600fe6004e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 1 Mar 2014 21:22:01 +0100 Subject: [PATCH 141/638] Tweaking dispatchScript method to be in line with the rest, and add devMode to *_AUTOLOAD_DUMP events --- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- src/Composer/EventDispatcher/EventDispatcher.php | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index a0bf8c29f..3783bd22c 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -46,7 +46,7 @@ class AutoloadGenerator public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') { - $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP); + $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode); $filesystem = new Filesystem(); $filesystem->ensureDirectoryExists($config->get('vendor-dir')); @@ -228,7 +228,7 @@ EOF; fclose($targetLoader); unset($sourceLoader, $targetLoader); - $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP); + $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode); } public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index f1e94e6d5..fb607831d 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -75,13 +75,9 @@ class EventDispatcher * @param string $eventName The constant in ScriptEvents * @param Script\Event $event */ - public function dispatchScript($eventName, Script\Event $event = null) + public function dispatchScript($eventName, $devMode = false) { - if (null == $event) { - $event = new Script\Event($eventName, $this->composer, $this->io); - } - - $this->doDispatch($event); + $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode)); } /** From fff913d6de5ec8c4315f466cf203c3e0acc3218a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 1 Mar 2014 21:25:32 +0100 Subject: [PATCH 142/638] Update deps --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 9e8b0b7ca..45ccf5710 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "78b771e9b9f3c0181350f1d6ed8fa3c7", + "hash": "e68bf60f228ca192b8b492cb95a80fa7", "packages": [ { "name": "justinrainbow/json-schema", @@ -277,16 +277,16 @@ "packages-dev": [ { "name": "phpunit/php-code-coverage", - "version": "1.2.15", + "version": "1.2.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6ba4ed2895d538a039d5d5866edc4ec0424c7852" + "reference": "69e55e68481cf708a6db43aff0b504e31402fe27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ba4ed2895d538a039d5d5866edc4ec0424c7852", - "reference": "6ba4ed2895d538a039d5d5866edc4ec0424c7852", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/69e55e68481cf708a6db43aff0b504e31402fe27", + "reference": "69e55e68481cf708a6db43aff0b504e31402fe27", "shasum": "" }, "require": { @@ -334,7 +334,7 @@ "testing", "xunit" ], - "time": "2014-02-03 07:44:47" + "time": "2014-02-25 03:34:05" }, { "name": "phpunit/php-file-iterator", @@ -521,16 +521,16 @@ }, { "name": "phpunit/phpunit", - "version": "3.7.31", + "version": "3.7.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d24e9877331039582497052cc3c4d9f465b88210" + "reference": "2752cbb9ea5bd84c2811b34b6953f76965ec7a2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d24e9877331039582497052cc3c4d9f465b88210", - "reference": "d24e9877331039582497052cc3c4d9f465b88210", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2752cbb9ea5bd84c2811b34b6953f76965ec7a2f", + "reference": "2752cbb9ea5bd84c2811b34b6953f76965ec7a2f", "shasum": "" }, "require": { @@ -547,7 +547,7 @@ "symfony/yaml": "~2.0" }, "require-dev": { - "pear-pear/pear": "1.9.4" + "pear-pear.php.net/pear": "1.9.4" }, "suggest": { "ext-json": "*", @@ -591,7 +591,7 @@ "testing", "xunit" ], - "time": "2014-02-03 07:46:27" + "time": "2014-02-25 03:47:29" }, { "name": "phpunit/phpunit-mock-objects", From c5d1a3575c574b67dc2a4c08e103da8485c6519d Mon Sep 17 00:00:00 2001 From: Alessandro Biavati Date: Sun, 2 Mar 2014 16:01:39 -0800 Subject: [PATCH 143/638] Added `path` flag to show install path for installed packages --- src/Composer/Command/ShowCommand.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 4b5b6ee9c..dbf41ceb9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -50,6 +50,7 @@ class ShowCommand extends Command new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'), new InputOption('self', 's', InputOption::VALUE_NONE, 'Show the root package information'), new InputOption('name-only', 'N', InputOption::VALUE_NONE, 'List package names only'), + new InputOption('path', 'P', InputOption::VALUE_NONE, 'Show package paths'), )) ->setHelp(<<getOption('name-only') && $showVersion && ($nameLength + $versionLength + 3 <= $width); - $writeDescription = !$input->getOption('name-only') && ($nameLength + ($showVersion ? $versionLength : 0) + 24 <= $width); + $writePath = !$input->getOption('name-only') && $input->getOption('path'); + $writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion && ($nameLength + $versionLength + 3 <= $width); + $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); @@ -211,6 +213,11 @@ EOT } $output->write(' ' . $description); } + + if ($writePath) { + $path = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n"); + $output->write(' ' . $path); + } } else { $output->write($indent . $package); } From 9fda8cdc84cc488c5d3bb651db5f388bf3dd90c2 Mon Sep 17 00:00:00 2001 From: Pavel Puchkin Date: Wed, 5 Mar 2014 11:25:58 +1000 Subject: [PATCH 144/638] Type in comment --- src/Composer/Downloader/GzipDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 4444db369..fb4bce89d 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -50,7 +50,7 @@ class GzipDownloader extends ArchiveDownloader throw new \RuntimeException($processError); } - // Gzip version of PHP has built-in support of gzip functions + // Windows version of PHP has built-in support of gzip functions $archiveFile = gzopen($file, 'rb'); $targetFile = fopen($targetFilepath, 'wb'); while ($string = gzread($archiveFile, 4096)) { From 92e283973f74fd1994e6e4430e9212d50145478e Mon Sep 17 00:00:00 2001 From: Sylvain Filteau Date: Wed, 5 Mar 2014 20:30:09 -0500 Subject: [PATCH 145/638] Skip test requiring mbstring when it's not installed --- tests/Composer/Test/Json/JsonFormatterTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php index 60d1f0040..6e341d382 100644 --- a/tests/Composer/Test/Json/JsonFormatterTest.php +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -22,6 +22,10 @@ class JsonFormatterTest extends \PHPUnit_Framework_TestCase */ public function testUnicodeWithPrependedSlash() { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('Test requires the mbstring extension'); + } + $data = '"' . chr(92) . chr(92) . chr(92) . 'u0119"'; $encodedData = JsonFormatter::format($data, true, true); $expected = '34+92+92+196+153+34'; From 604a65cc31f3e5d8a2b96802135ac24434e87678 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 6 Mar 2014 10:26:16 +0100 Subject: [PATCH 146/638] Fix indenting, refs #2788 --- tests/Composer/Test/Json/JsonFormatterTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php index 6e341d382..ce7ceefa6 100644 --- a/tests/Composer/Test/Json/JsonFormatterTest.php +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -22,9 +22,9 @@ class JsonFormatterTest extends \PHPUnit_Framework_TestCase */ public function testUnicodeWithPrependedSlash() { - if (!extension_loaded('mbstring')) { - $this->markTestSkipped('Test requires the mbstring extension'); - } + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('Test requires the mbstring extension'); + } $data = '"' . chr(92) . chr(92) . chr(92) . 'u0119"'; $encodedData = JsonFormatter::format($data, true, true); From 634d1aba63b06238c89ba1ef3e16d2544701a81b Mon Sep 17 00:00:00 2001 From: Eric Stern Date: Thu, 6 Mar 2014 10:15:59 -0800 Subject: [PATCH 147/638] Add @generated annotation to lockfile readme, giving a hint for linters and other static analysis tools --- src/Composer/Package/Locker.php | 4 +++- tests/Composer/Test/Package/LockerTest.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index a928584e4..88ac547ba 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -210,7 +210,9 @@ class Locker public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags) { $lock = array( - '_readme' => array('This file locks the dependencies of your project to a known state', 'Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file'), + '_readme' => array('This file locks the dependencies of your project to a known state', + 'Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file', + 'This file is @gener'.'ated automatically'), 'hash' => $this->hash, 'packages' => null, 'packages-dev' => null, diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 1b879f421..b530eaf41 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -120,7 +120,9 @@ class LockerTest extends \PHPUnit_Framework_TestCase ->expects($this->once()) ->method('write') ->with(array( - '_readme' => array('This file locks the dependencies of your project to a known state', 'Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file'), + '_readme' => array('This file locks the dependencies of your project to a known state', + 'Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file', + 'This file is @gener'.'ated automatically'), 'hash' => 'md5', 'packages' => array( array('name' => 'pkg1', 'version' => '1.0.0-beta'), From b639005f2975378b72dc915b857b84f36a2985df Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Fri, 21 Feb 2014 16:26:53 +0100 Subject: [PATCH 148/638] Added a failing test for #2626 --- .../Fixtures/installer/replace-alias.test | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/replace-alias.test diff --git a/tests/Composer/Test/Fixtures/installer/replace-alias.test b/tests/Composer/Test/Fixtures/installer/replace-alias.test new file mode 100644 index 000000000..c3660cd6b --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/replace-alias.test @@ -0,0 +1,24 @@ +--TEST-- +Ensure a replacer package deals with branch aliases +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "dev-master", "replace": {"c/c": "self.version" }, "extra": { "branch-alias": {"dev-master": "1.0.x-dev"} } }, + { "name": "b/b", "version": "1.0.0", "require": {"c/c": "1.*"} }, + { "name": "c/c", "version": "dev-master", "extra": { "branch-alias": {"dev-master": "1.0.x-dev"} } } + ] + } + ], + "require": { + "a/a": "dev-master", + "b/b": "1.*" + } +} +--RUN-- +install +--EXPECT-- +Installing a/a (dev-master) +Installing b/b (1.0.0) From 224132893490f57e9aa6f9b7c89f8e9de70f6372 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 12 Mar 2014 15:12:34 +0100 Subject: [PATCH 149/638] fix "replace-alias.test" test --- tests/Composer/Test/Fixtures/installer/replace-alias.test | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/Fixtures/installer/replace-alias.test b/tests/Composer/Test/Fixtures/installer/replace-alias.test index c3660cd6b..327fb5ab5 100644 --- a/tests/Composer/Test/Fixtures/installer/replace-alias.test +++ b/tests/Composer/Test/Fixtures/installer/replace-alias.test @@ -21,4 +21,5 @@ Ensure a replacer package deals with branch aliases install --EXPECT-- Installing a/a (dev-master) +Marking a/a (1.0.x-dev) as installed, alias of a/a (dev-master) Installing b/b (1.0.0) From 0e9325da796a5a3b990a2d756ea5e5a2376841bd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 12 Mar 2014 16:30:09 +0100 Subject: [PATCH 150/638] for AliasPackages check that the aliased package is white listed so that version constraints of AliasPackages are taken into account when computing package provisions --- src/Composer/DependencyResolver/Pool.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 18f2d5797..853673905 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -268,9 +268,18 @@ class Pool $nameMatch = false; foreach ($candidates as $candidate) { + $aliasOfCandidate = null; + + // alias packages are not white listed, make sure that the package + // being aliased is white listed + if ($candidate instanceof AliasPackage) { + $aliasOfCandidate = $candidate->getAliasOf(); + } + if ($this->whitelist !== null && ( (is_array($candidate) && isset($candidate['id']) && !isset($this->whitelist[$candidate['id']])) || - (is_object($candidate) && !isset($this->whitelist[$candidate->getId()])) + (is_object($candidate) && !($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->getId()])) || + (is_object($candidate) && $candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->getId()])) )) { continue; } From 454c6eca81d818b51bb58ceb4c03f10c403630e5 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Fri, 14 Mar 2014 14:45:31 -0500 Subject: [PATCH 151/638] added new dependency to Perforce object, updating some tests. --- .../Downloader/PerforceDownloader.php | 4 +- .../Repository/Vcs/PerforceDriver.php | 2 +- src/Composer/Util/Perforce.php | 21 ++- .../Downloader/PerforceDownloaderTest.php | 160 +++++++++++------- tests/Composer/Test/Util/PerforceTest.php | 11 +- 5 files changed, 122 insertions(+), 76 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 2bb1ba619..632ac648c 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -22,7 +22,7 @@ use Composer\Util\Perforce; class PerforceDownloader extends VcsDownloader { protected $perforce; - protected $perforceInjected = false; +// protected $perforceInjected = false; /** * {@inheritDoc} @@ -54,7 +54,7 @@ class PerforceDownloader extends VcsDownloader if ($repository instanceof VcsRepository) { $repoConfig = $this->getRepoConfig($repository); } - $this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path); + $this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path, $this->process, $this->io); } private function getRepoConfig(VcsRepository $repository) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 79500f1d6..8a1b40623 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -56,7 +56,7 @@ class PerforceDriver extends VcsDriver } $repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot; - $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process); + $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io); } /** diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 94b9730eb..c3b66329f 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -35,27 +35,36 @@ class Perforce protected $windowsFlag; protected $commandResult; - public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows) + protected $io; + + public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows, IOInterface $io) { $this->windowsFlag = $isWindows; $this->p4Port = $port; $this->initializePath($path); $this->process = $process; $this->initialize($repoConfig); + $this->io = $io; } - public static function create($repoConfig, $port, $path, ProcessExecutor $process = null) + public static function create($repoConfig, $port, $path, ProcessExecutor $process = null, IOInterface $io) { if (!isset($process)) { $process = new ProcessExecutor; } $isWindows = defined('PHP_WINDOWS_VERSION_BUILD'); - $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows); + $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io); return $perforce; } + public static function checkServerExists($url, ProcessExecutor $processExecutor) + { + $output = null; + return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output); + } + public function initialize($repoConfig) { $this->uniquePerforceClientName = $this->generateUniquePerforceClientName(); @@ -382,12 +391,6 @@ class Perforce } } - public static function checkServerExists($url, ProcessExecutor $processExecutor) - { - $output = null; - return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output); - } - public function getComposerInformation($identifier) { $index = strpos($identifier, '@'); diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 8a20f67cc..0eb981362 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -15,86 +15,120 @@ namespace Composer\Test\Downloader; use Composer\Downloader\PerforceDownloader; use Composer\Config; use Composer\Repository\VcsRepository; +use Composer\IO\IOInterface; /** * @author Matt Whittom */ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase { - private $io; - private $config; - private $testPath; - public static $repository; + + protected $config; + protected $downloader; + protected $io; + protected $package; + protected $processExecutor; + protected $repoConfig; + protected $repository; + protected $testPath; - protected function setUp() + public function setUp() { - $this->testPath = sys_get_temp_dir() . '/composer-test'; - $this->config = new Config(); - $this->config->merge( - array( - 'config' => array( - 'home' => $this->testPath, - ), - ) - ); - $this->io = $this->getMock('Composer\IO\IOInterface'); + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->repoConfig = $this->getRepoConfig(); + $this->config = $this->getConfig(); + $this->io = $this->getMockIoInterface(); + $this->processExecutor = $this->getMockProcessExecutor(); + $this->repository = $this->getMockRepository($this->repoConfig, $this->io, $this->config); + $this->package = $this->getMockPackageInterface($this->repository); + $this->downloader = new PerforceDownloader($this->io, $this->config, $this->processExecutor); } - public function testInitPerforceGetRepoConfig() + public function tearDown() + { + $this->downloader = null; + $this->package = null; + $this->repository = null; + $this->io = null; + $this->config = null; + $this->repoConfig = null; + $this->testPath = null; + } + + protected function getMockProcessExecutor() + { + return $this->getMock('Composer\Util\ProcessExecutor'); + } + + protected function getConfig() + { + $config = new Config(); + $settings = array('config' => array('home' => $this->testPath)); + $config->merge($settings); + return $config; + } + + protected function getMockIoInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + + protected function getMockPackageInterface(VcsRepository $repository) { - $downloader = new PerforceDownloader($this->io, $this->config); $package = $this->getMock('Composer\Package\PackageInterface'); - $repoConfig = array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); - $repository = $this->getMock( - 'Composer\Repository\VcsRepository', - array('getRepoConfig'), - array($repoConfig, $this->io, $this->config) - ); - $package->expects($this->at(0)) - ->method('getRepository') - ->will($this->returnValue($repository)); - $repository->expects($this->at(0)) - ->method('getRepoConfig'); - $path = $this->testPath; - $downloader->initPerforce($package, $path, 'SOURCE_REF'); + $package->expects($this->any())->method('getRepository')->will($this->returnValue($repository)); + return $package; } + protected function getRepoConfig() + { + return array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); + } + + protected function getMockRepository(array $repoConfig, IOInterface $io, Config $config) + { + $class = 'Composer\Repository\VcsRepository'; + $methods = array('getRepoConfig'); + $args = array($repoConfig, $io, $config); + $repository = $this->getMock($class, $methods, $args); + $repository->expects($this->any())->method('getRepoConfig')->will($this->returnValue($repoConfig)); + return $repository; + } + + public function testInitPerforceInstantiatesANewPerforceObject() + { + $this->downloader->initPerforce($this->package, $this->testPath, 'SOURCE_REF'); + } + + public function testInitPerforceDoesNothingIfPerforceAlreadySet() + { + $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock(); + $this->downloader->setPerforce($perforce); + $this->repository->expects($this->never())->method('getRepoConfig'); + $this->downloader->initPerforce($this->package, $this->testPath, 'SOURCE_REF'); + } + + /** + * @depends testInitPerforceInstantiatesANewPerforceObject + * @depends testInitPerforceDoesNothingIfPerforceAlreadySet + */ public function testDoDownload() { - $downloader = new PerforceDownloader($this->io, $this->config); - $repoConfig = array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH', 'p4user' => 'TEST_USER'); - $port = 'TEST_PORT'; - $path = 'TEST_PATH'; - $process = $this->getmock('Composer\Util\ProcessExecutor'); - $perforce = $this->getMock( - 'Composer\Util\Perforce', - array('setStream', 'queryP4User', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase'), - array($repoConfig, $port, $path, $process, true, 'TEST') - ); + //I really don't like this test but the logic of each Perforce method is tested in the Perforce class. Really I am just enforcing workflow. $ref = 'SOURCE_REF'; $label = 'LABEL'; - $perforce->expects($this->at(0)) - ->method('setStream') - ->with($this->equalTo($ref)); - $perforce->expects($this->at(1)) - ->method('queryP4User') - ->with($this->io); - $perforce->expects($this->at(2)) - ->method('writeP4ClientSpec'); - $perforce->expects($this->at(3)) - ->method('connectClient'); - $perforce->expects($this->at(4)) - ->method('syncCodeBase') - ->with($this->equalTo($label)); - $downloader->setPerforce($perforce); - $package = $this->getMock('Composer\Package\PackageInterface'); - $package->expects($this->at(0)) - ->method('getSourceReference') - ->will($this->returnValue($ref)); - $package->expects($this->at(1)) - ->method('getPrettyVersion') - ->will($this->returnValue($label)); - $path = $this->testPath; - $downloader->doDownload($package, $path); + $this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref)); + $this->package->expects($this->once())->method('getPrettyVersion')->will($this->returnValue($label)); + $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); + $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); + $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); + $perforce->expects($this->at(0))->method('setStream')->with($this->equalTo($ref)); + $perforce->expects($this->at(1))->method('p4Login')->with($this->identicalTo($this->io)); + $perforce->expects($this->at(2))->method('writeP4ClientSpec'); + $perforce->expects($this->at(3))->method('connectClient'); + $perforce->expects($this->at(4))->method('syncCodeBase'); + $perforce->expects($this->at(5))->method('cleanupClientSpec'); + $this->downloader->setPerforce($perforce); + $this->downloader->doDownload($this->package, $this->testPath); } } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index f2eeb1964..56a234536 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -22,6 +22,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { protected $perforce; protected $processExecutor; + protected $io; public function setUp() { @@ -32,7 +33,15 @@ class PerforceTest extends \PHPUnit_Framework_TestCase 'p4user' => 'user', 'unique_perforce_client_name' => 'TEST' ); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true); + $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, $io); + } + + public function tearDown() + { + $this->perforce = null; + $this->io = null; + $this->processExecutor = null; } public function testGetClientWithoutStream() From 34a68f4020e0b7cc3317c0f747c7738acd4d27e9 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Mon, 17 Mar 2014 16:06:19 -0500 Subject: [PATCH 152/638] almost all unit tests passing after IOInterface dependency refactor. no longer passing IOInterface into any Perforce methods --- .../Downloader/PerforceDownloader.php | 2 +- .../Repository/Vcs/PerforceDriver.php | 8 +- src/Composer/Util/Perforce.php | 29 ++- .../Downloader/PerforceDownloaderTest.php | 13 +- .../Repository/Vcs/PerforceDriverTest.php | 186 +++++++------- tests/Composer/Test/Util/PerforceTest.php | 230 ++++++++---------- 6 files changed, 239 insertions(+), 229 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 632ac648c..da70c3469 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -44,7 +44,7 @@ class PerforceDownloader extends VcsDownloader public function initPerforce($package, $path) { - if ($this->perforce) { + if (!empty($this->perforce)) { $this->perforce->initializePath($path); return; } diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 8a1b40623..6a55033a1 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -35,7 +35,7 @@ class PerforceDriver extends VcsDriver { $this->depot = $this->repoConfig['depot']; $this->branch = ''; - if (isset($this->repoConfig['branch'])) { + if (!empty($this->repoConfig['branch'])) { $this->branch = $this->repoConfig['branch']; } @@ -51,7 +51,7 @@ class PerforceDriver extends VcsDriver private function initPerforce($repoConfig) { - if (isset($this->perforce)) { + if (!empty($this->perforce)) { return; } @@ -64,7 +64,7 @@ class PerforceDriver extends VcsDriver */ public function getComposerInformation($identifier) { - if (isset($this->composerInfoIdentifier)) { + if (!empty($this->composerInfoIdentifier)) { if (strcmp($identifier, $this->composerInfoIdentifier) === 0) { return $this->composerInfo; } @@ -141,7 +141,7 @@ class PerforceDriver extends VcsDriver $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; $result = false; - if (isset($this->composerInfo)) { + if (!empty($this->composerInfo)) { $result = count($this->composerInfo) > 0; } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index c3b66329f..2a1094e59 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -200,7 +200,12 @@ class Perforce return $this->p4User; } - public function queryP4User(IOInterface $io) + public function setUser($user) + { + $this->p4User = $user; + } + + public function queryP4User() { $this->getUser(); if (strlen($this->p4User) > 0) { @@ -210,7 +215,7 @@ class Perforce if (strlen($this->p4User) > 0) { return; } - $this->p4User = $io->ask('Enter P4 User:'); + $this->p4User = $this->io->ask('Enter P4 User:'); if ($this->windowsFlag) { $command = 'p4 set P4USER=' . $this->p4User; } else { @@ -248,14 +253,14 @@ class Perforce } } - public function queryP4Password(IOInterface $io) + public function queryP4Password() { if (isset($this->p4Password)) { return $this->p4Password; } $password = $this->getP4variable('P4PASSWD'); if (strlen($password) <= 0) { - $password = $io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); + $password = $this->io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); } $this->p4Password = $password; @@ -365,6 +370,16 @@ class Perforce return; } + public function getWindowsFlag() + { + return $this->windowsFlag; + } + + public function setWindowsFlag($flag) + { + $this->windowsFlag = $flag; + } + public function windowsLogin($password) { $command = $this->generateP4Command(' login -a'); @@ -373,11 +388,11 @@ class Perforce return $process->run(); } - public function p4Login(IOInterface $io) + public function p4Login() { - $this->queryP4User($io); + $this->queryP4User(); if (!$this->isLoggedIn()) { - $password = $this->queryP4Password($io); + $password = $this->queryP4Password(); if ($this->windowsFlag) { $this->windowsLogin($password); } else { diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 0eb981362..c78f1ed13 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -122,12 +122,13 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); - $perforce->expects($this->at(0))->method('setStream')->with($this->equalTo($ref)); - $perforce->expects($this->at(1))->method('p4Login')->with($this->identicalTo($this->io)); - $perforce->expects($this->at(2))->method('writeP4ClientSpec'); - $perforce->expects($this->at(3))->method('connectClient'); - $perforce->expects($this->at(4))->method('syncCodeBase'); - $perforce->expects($this->at(5))->method('cleanupClientSpec'); + $perforce->expects($this->at(0))->method('initializePath')->with($this->equalTo($this->testPath)); + $perforce->expects($this->at(1))->method('setStream')->with($this->equalTo($ref)); + $perforce->expects($this->at(2))->method('p4Login')->with($this->identicalTo($this->io)); + $perforce->expects($this->at(3))->method('writeP4ClientSpec'); + $perforce->expects($this->at(4))->method('connectClient'); + $perforce->expects($this->at(5))->method('syncCodeBase'); + $perforce->expects($this->at(6))->method('cleanupClientSpec'); $this->downloader->setPerforce($perforce); $this->downloader->doDownload($this->package, $this->testPath); } diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 36cd69ebc..bc228de7e 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -21,113 +21,129 @@ use Composer\Config; */ class PerforceDriverTest extends \PHPUnit_Framework_TestCase { - private $config; - private $io; - private $process; - private $remoteFileSystem; - private $testPath; + protected $config; + protected $io; + protected $process; + protected $remoteFileSystem; + protected $testPath; + protected $driver; + protected $repoConfig; + + const TEST_URL = 'TEST_PERFORCE_URL'; + const TEST_DEPOT = 'TEST_DEPOT_CONFIG'; + const TEST_BRANCH = 'TEST_BRANCH_CONFIG'; public function setUp() { - $this->testPath = sys_get_temp_dir() . '/composer-test'; - $this->config = new Config(); - $this->config->merge( - array( - 'config' => array( - 'home' => $this->testPath, - ), - ) - ); - - $this->io = $this->getMock('Composer\IO\IOInterface'); - $this->process = $this->getMock('Composer\Util\ProcessExecutor'); - $this->remoteFileSystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor() - ->getMock(); + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->config = $this->getTestConfig($this->testPath); + $this->repoConfig = $this->getTestRepoConfig(); + $this->io = $this->getMockIOInterface(); + $this->process = $this->getMockProcessExecutor(); + $this->remoteFileSystem = $this->getMockRemoteFilesystem(); + $this->perforce = $this->getMockPerforce(); + $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); } public function tearDown() { + //cleanup directory under test path $fs = new Filesystem; $fs->removeDirectory($this->testPath); + $this->driver = null; + $this->perforce = null; + $this->remoteFileSystem = null; + $this->process = null; + $this->io = null; + $this->repoConfig = null; + $this->config = null; + $this->testPath = null; + } + + protected function getTestConfig($testPath) + { + $config = new Config(); + $config->merge(array('config'=>array('home'=>$testPath))); + return $config; + } + + protected function getTestRepoConfig() + { + return array( + 'url' => self::TEST_URL, + 'depot' => self::TEST_DEPOT, + 'branch' => self::TEST_BRANCH, + ); + } + + protected function getMockIOInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + + protected function getMockProcessExecutor() + { + return $this->getMock('Composer\Util\ProcessExecutor'); + } + + protected function getMockRemoteFilesystem() + { + return $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock(); + } + + protected function getMockPerforce() + { + $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation'); + return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock(); } public function testInitializeCapturesVariablesFromRepoConfig() { - $this->setUp(); - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $process = $this->getMock('Composer\Util\ProcessExecutor'); - $arguments = array( - array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), - 'port' => 'TEST_PORT', - 'path' => $this->testPath, - $process, - true, - 'TEST' - ); - $perforce = $this->getMock('Composer\Util\Perforce', null, $arguments); - $driver->setPerforce($perforce); + $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $driver->setPerforce($this->perforce); $driver->initialize(); - $this->assertEquals('TEST_PERFORCE_URL', $driver->getUrl()); - $this->assertEquals('TEST_DEPOT_CONFIG', $driver->getDepot()); - $this->assertEquals('TEST_BRANCH_CONFIG', $driver->getBranch()); + $this->assertEquals(self::TEST_URL, $driver->getUrl()); + $this->assertEquals(self::TEST_DEPOT, $driver->getDepot()); + $this->assertEquals(self::TEST_BRANCH, $driver->getBranch()); } public function testInitializeLogsInAndConnectsClient() { - $this->setUp(); - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock(); - $perforce->expects($this->at(0)) - ->method('p4Login') - ->with($this->io); - $perforce->expects($this->at(1)) - ->method('checkStream') - ->with($this->equalTo('TEST_DEPOT_CONFIG')); - $perforce->expects($this->at(2)) - ->method('writeP4ClientSpec'); - $perforce->expects($this->at(3)) - ->method('connectClient'); - - $driver->setPerforce($perforce); - $driver->initialize(); + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('p4Login')->with($this->identicalTo($this->io)); + $this->perforce->expects($this->at(1))->method('checkStream')->with($this->equalTo(self::TEST_DEPOT)); + $this->perforce->expects($this->at(2))->method('writeP4ClientSpec'); + $this->perforce->expects($this->at(3))->method('connectClient'); + $this->driver->initialize(); } - public function testHasComposerFile() + /** + * @depends testInitializeCapturesVariablesFromRepoConfig + * @depends testInitializeLogsInAndConnectsClient + */ + public function testHasComposerFileReturnsFalseOnNoComposerFile() { - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $process = $this->getMock('Composer\Util\ProcessExecutor'); - $arguments = array( - array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), - 'port' => 'TEST_PORT', - 'path' => $this->testPath, - $process, - true, - 'TEST' - ); - $perforce = $this->getMock('Composer\Util\Perforce', array('getComposerInformation'), $arguments); - $perforce->expects($this->at(0)) - ->method('getComposerInformation') - ->with($this->equalTo('//TEST_DEPOT_CONFIG/TEST_IDENTIFIER')) - ->will($this->returnValue('Some json stuff')); - $driver->setPerforce($perforce); - $driver->initialize(); $identifier = 'TEST_IDENTIFIER'; - $result = $driver->hasComposerFile($identifier); + $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); + $this->driver->initialize(); + $result = $this->driver->hasComposerFile($identifier); + $this->assertFalse($result); + } + + /** + * @depends testInitializeCapturesVariablesFromRepoConfig + * @depends testInitializeLogsInAndConnectsClient + */ + public function testHasComposerFileReturnsTrueWithOneOrMoreComposerFiles() + { + $identifier = 'TEST_IDENTIFIER'; + $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); + $this->driver->initialize(); + $result = $this->driver->hasComposerFile($identifier); $this->assertTrue($result); } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 56a234536..b7865d5e6 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -24,26 +24,44 @@ class PerforceTest extends \PHPUnit_Framework_TestCase protected $processExecutor; protected $io; + const TEST_DEPOT = 'depot'; + const TEST_BRANCH = 'branch'; + const TEST_P4USER = 'user'; + const TEST_CLIENT_NAME = 'TEST'; + const TEST_PORT = 'port'; + const TEST_PATH = 'path'; + public function setUp() { $this->processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $repoConfig = array( - 'depot' => 'depot', - 'branch' => 'branch', - 'p4user' => 'user', - 'unique_perforce_client_name' => 'TEST' - ); - $io = $this->getMock('Composer\IO\IOInterface'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, $io); + $this->repoConfig = $this->getTestRepoConfig(); + $this->io = $this->getMockIOInterface(); + $this->perforce = new Perforce($this->repoConfig, self::TEST_PORT, self::TEST_PATH, $this->processExecutor, true, $this->io); } public function tearDown() { - $this->perforce = null; - $this->io = null; + $this->perforce = null; + $this->io = null; + $this->repoConfig = null; $this->processExecutor = null; } + public function getTestRepoConfig() + { + return array( + 'depot' => self::TEST_DEPOT, + 'branch' => self::TEST_BRANCH, + 'p4user' => self::TEST_P4USER, + 'unique_perforce_client_name' => self::TEST_CLIENT_NAME + ); + } + + public function getMockIOInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + public function testGetClientWithoutStream() { $client = $this->perforce->getClient(); @@ -107,116 +125,90 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserWithUserAlreadySet() { - $io = $this->getMock('Composer\IO\IOInterface'); - - $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'TEST_USER'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $this->perforce->queryP4user($io); - $this->assertEquals('TEST_USER', $this->perforce->getUser()); + $this->perforce->queryP4user(); + $this->assertEquals(self::TEST_P4USER, $this->perforce->getUser()); } public function testQueryP4UserWithUserSetInP4VariablesWithWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(true); $expectedCommand = 'p4 set'; + $callback = function($command, &$output) + { + $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL ; - - return true; - } - ) - ); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $this->perforce->queryP4user(); $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); } public function testQueryP4UserWithUserSetInP4VariablesNotWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(false); $expectedCommand = 'echo $P4USER'; + $callback = function($command, &$output) + { + $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; - - return true; - } - ) - ); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $this->perforce->queryP4user(); $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); } public function testQueryP4UserQueriesForUser() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - - $this->perforce->queryP4user($io); + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); + $this->perforce->queryP4user(); $this->assertEquals('TEST_QUERY_USER', $this->perforce->getUser()); } public function testQueryP4UserStoresResponseToQueryForUserWithWindows() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(true); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; + $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); $this->processExecutor->expects($this->at(1)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will($this->returnValue(0)); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + $this->perforce->queryP4user(); } public function testQueryP4UserStoresResponseToQueryForUserWithoutWindows() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(false); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - $expectedCommand = 'export P4USER=TEST_QUERY_USER'; + $expectedCommand = 'export P4USER=TEST_QUERY_USER'; + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); $this->processExecutor->expects($this->at(1)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will($this->returnValue(0)); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + $this->perforce->queryP4user(); } public function testQueryP4PasswordWithPasswordAlreadySet() @@ -227,69 +219,55 @@ class PerforceTest extends \PHPUnit_Framework_TestCase 'p4user' => 'user', 'p4password' => 'TEST_PASSWORD' ); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - $io = $this->getMock('Composer\IO\IOInterface'); - - $password = $this->perforce->queryP4Password($io); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, $this->getMockIOInterface(), 'TEST'); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_PASSWORD', $password); } public function testQueryP4PasswordWithPasswordSetInP4VariablesWithWindowsOS() { - $io = $this->getMock('Composer\IO\IOInterface'); - + $this->perforce->setWindowsFlag(true); $expectedCommand = 'p4 set'; + $callback = function($command, &$output) + { + $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; - - return true; - } - ) - ); - - $password = $this->perforce->queryP4Password($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); } public function testQueryP4PasswordWithPasswordSetInP4VariablesNotWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'user'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setWindowsFlag(false); $expectedCommand = 'echo $P4PASSWD'; + $callback = function($command, &$output) + { + $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); - return true; - } - ) - ); - - $password = $this->perforce->queryP4Password($io); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); } public function testQueryP4PasswordQueriesForPassword() { - $io = $this->getMock('Composer\IO\IOInterface'); $expectedQuestion = 'Enter password for Perforce user user: '; - $io->expects($this->at(0)) + $this->io->expects($this->at(0)) ->method('askAndHideAnswer') ->with($this->equalTo($expectedQuestion)) ->will($this->returnValue('TEST_QUERY_PASSWORD')); - $password = $this->perforce->queryP4Password($io); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_QUERY_PASSWORD', $password); } From c6133050b32e983a0bdfe5ea3ff396f6d761fa42 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 18 Mar 2014 13:52:54 -0500 Subject: [PATCH 153/638] got all unit tests passing --- src/Composer/Repository/Vcs/PerforceDriver.php | 2 ++ src/Composer/Util/Perforce.php | 2 +- tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 6a55033a1..535a79516 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -141,6 +141,8 @@ class PerforceDriver extends VcsDriver $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; $result = false; + return !empty($this->composerInfo); + if (!empty($this->composerInfo)) { $result = count($this->composerInfo) > 0; } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 2a1094e59..ecafaf6f0 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -47,7 +47,7 @@ class Perforce $this->io = $io; } - public static function create($repoConfig, $port, $path, ProcessExecutor $process = null, IOInterface $io) + public static function create($repoConfig, $port, $path, ProcessExecutor $process, IOInterface $io) { if (!isset($process)) { $process = new ProcessExecutor; diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index bc228de7e..7666efebc 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -126,7 +126,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; $this->driver->setPerforce($this->perforce); - $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); + $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); $this->assertFalse($result); @@ -141,7 +141,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; $this->driver->setPerforce($this->perforce); - $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); + $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); $this->assertTrue($result); From 14a6406f9b0a61bbbaeb6b25ada865e06be03f34 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 18 Mar 2014 14:39:47 -0500 Subject: [PATCH 154/638] Fixing bug not cleaning up workspaces. --- .../Repository/Vcs/PerforceDriver.php | 6 +++++ src/Composer/Util/Perforce.php | 24 ++++++++++++++++--- .../Repository/Vcs/PerforceDriverTest.php | 11 ++++++++- tests/Composer/Test/Util/PerforceTest.php | 15 ++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 535a79516..090de1bb8 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -193,4 +193,10 @@ class PerforceDriver extends VcsDriver { $this->perforce = $perforce; } + + public function getPerforce() + { + return $this->perforce; + } + } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index ecafaf6f0..659cbfc67 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -37,6 +37,8 @@ class Perforce protected $io; + protected $filesystem; + public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows, IOInterface $io) { $this->windowsFlag = $isWindows; @@ -109,10 +111,10 @@ class Perforce public function cleanupClientSpec() { $client = $this->getClient(); - $command = 'p4 client -d $client'; + $command = 'p4 client -d ' . $client; $this->executeCommand($command); $clientSpec = $this->getP4ClientSpec(); - $fileSystem = new FileSystem($this->process); + $fileSystem = $this->getFilesystem(); $fileSystem->remove($clientSpec); } @@ -141,7 +143,7 @@ class Perforce public function initializePath($path) { $this->path = $path; - $fs = new Filesystem(); + $fs = $this->getFilesystem(); $fs->ensureDirectoryExists($path); } @@ -559,4 +561,20 @@ class Perforce return $result; } + + public function getFilesystem() + { + if (empty($this->filesystem)) + { + $this->filesystem = new Filesystem($this->process); + } + return $this->filesystem; + } + + + public function setFilesystem(Filesystem $fs) + { + $this->filesystem = $fs; + } + } diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 7666efebc..9afc57d30 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -93,7 +93,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase protected function getMockPerforce() { - $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation'); + $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation', 'cleanupClientSpec'); return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock(); } @@ -159,4 +159,13 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->expectOutputString(''); $this->assertFalse(PerforceDriver::supports($this->io, $this->config, 'existing.url')); } + + public function testCleanup() + { + $this->perforce->expects($this->once())->method('cleanupClientSpec'); + $this->driver->setPerforce($this->perforce); + $this->driver->cleanup(); + $this->assertNull($this->driver->getPerforce()); + } + } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index b7865d5e6..65857343b 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -679,4 +679,19 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { $this->perforce->setStream('//depot/branch'); } + + public function testCleanupClientSpecShouldDeleteClient() + { + $fs = $this->getMock('Composer\Util\Filesystem'); + $this->perforce->setFilesystem($fs); + + $testClient = $this->perforce->getClient(); + $expectedCommand = 'p4 client -d ' . $testClient; + $this->processExecutor->expects($this->once())->method('execute')->with($this->equalTo($expectedCommand)); + + $fs->expects($this->once())->method('remove')->with($this->perforce->getP4ClientSpec()); + + $this->perforce->cleanupClientSpec(); + } + } From 734e5e2f2bc6ab31532e56cec622ce0201a02089 Mon Sep 17 00:00:00 2001 From: Fred Emmott Date: Tue, 18 Mar 2014 14:37:37 -0700 Subject: [PATCH 155/638] Add autoload support for .hh files (HHVM) HHVM is adding support for an alternative extension for files using HHVM-specific features. Support them in the Class Map and PSR4 autoloaders. Trivial example: https://github.com/fredemmott/hh_extension_toolset --- src/Composer/Autoload/ClassLoader.php | 21 +++++++++++++++++---- src/Composer/Autoload/ClassMapGenerator.php | 4 ++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 47ae2ee92..150cfc881 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -291,8 +291,22 @@ class ClassLoader return $this->classMap[$class]; } + $file = $this->findFileWithExtension($class, '.php'); + if ($file === null) { + // Indicates a Hack file (hacklang.org) + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + return $file; + } + + private function findFileWithExtension($class, $ext) { // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { @@ -321,7 +335,7 @@ class ClassLoader . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { @@ -348,8 +362,7 @@ class ClassLoader return $file; } - // Remember that this class does not exist. - return $this->classMap[$class] = false; + return null; } } diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 73c036a37..1f20274e2 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -54,7 +54,7 @@ class ClassMapGenerator if (is_file($path)) { $path = array(new \SplFileInfo($path)); } elseif (is_dir($path)) { - $path = Finder::create()->files()->followLinks()->name('/\.(php|inc)$/')->in($path); + $path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path); } else { throw new \RuntimeException( 'Could not scan for classes inside "'.$path. @@ -68,7 +68,7 @@ class ClassMapGenerator foreach ($path as $file) { $filePath = $file->getRealPath(); - if (!in_array(pathinfo($filePath, PATHINFO_EXTENSION), array('php', 'inc'))) { + if (!in_array(pathinfo($filePath, PATHINFO_EXTENSION), array('php', 'inc', 'hh'))) { continue; } From 7d3274224aa5e4b1b7efb27a3fb02291546b9590 Mon Sep 17 00:00:00 2001 From: Fred Emmott Date: Tue, 18 Mar 2014 16:21:41 -0700 Subject: [PATCH 156/638] Only look for .hh files at runtime if using HHVM The ClassMap generator will look regardless, but that's not a runtime cost, so seems desirable (in case composer install/update needs to be called with php5 for some resaon) --- src/Composer/Autoload/ClassLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 150cfc881..e27edeb1d 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -292,7 +292,7 @@ class ClassLoader } $file = $this->findFileWithExtension($class, '.php'); - if ($file === null) { + if ($file === null && defined('HHVM_VERSION')) { // Indicates a Hack file (hacklang.org) $file = $this->findFileWithExtension($class, '.hh'); } From 16489dc08435ff9f51bb403084e35fa4dffb03be Mon Sep 17 00:00:00 2001 From: Sebastian Brandt Date: Wed, 19 Mar 2014 17:00:30 +0100 Subject: [PATCH 157/638] Update SvnDownloader.php added --ignore-ancestry to the switch statement because it can be a problem with svn:properties --- src/Composer/Downloader/SvnDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 8094a00dd..c31c3df6b 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -48,7 +48,7 @@ class SvnDownloader extends VcsDownloader } $this->io->write(" Checking out " . $ref); - $this->execute($url, "svn switch", sprintf("%s/%s", $url, $ref), $path); + $this->execute($url, "svn switch --ignore-ancestry", sprintf("%s/%s", $url, $ref), $path); } /** From c1a8b4645cc9178ed99b645b1fc9069d7659b525 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Wed, 19 Mar 2014 13:58:41 -0500 Subject: [PATCH 158/638] Fixing delete client workspace bug. --- src/Composer/Util/Perforce.php | 4 +++- tests/Composer/Test/Util/PerforceTest.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 659cbfc67..f3e9d2023 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -111,7 +111,9 @@ class Perforce public function cleanupClientSpec() { $client = $this->getClient(); - $command = 'p4 client -d ' . $client; + $task = 'client -d ' . $client; + $useP4Client = false; + $command = $this->generateP4Command($task, $useP4Client); $this->executeCommand($command); $clientSpec = $this->getP4ClientSpec(); $fileSystem = $this->getFilesystem(); diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 65857343b..06e3cce8b 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -686,7 +686,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $this->perforce->setFilesystem($fs); $testClient = $this->perforce->getClient(); - $expectedCommand = 'p4 client -d ' . $testClient; + $expectedCommand = 'p4 -u ' . self::TEST_P4USER . ' -p ' . self::TEST_PORT . ' client -d ' . $testClient; $this->processExecutor->expects($this->once())->method('execute')->with($this->equalTo($expectedCommand)); $fs->expects($this->once())->method('remove')->with($this->perforce->getP4ClientSpec()); From 13b57112a9fe467eeb9df2e351a31c6f790d54b9 Mon Sep 17 00:00:00 2001 From: Jan Brecka Date: Thu, 20 Mar 2014 13:37:05 +0100 Subject: [PATCH 159/638] Throw RuntimeException when ambiguous reference was found. --- src/Composer/Autoload/ClassMapGenerator.php | 4 ++++ .../Test/Autoload/ClassMapGeneratorTest.php | 16 ++++++++++++++++ .../Test/Autoload/Fixtures/Ambiguous/A.php | 6 ++++++ .../Test/Autoload/Fixtures/Ambiguous/other/A.php | 6 ++++++ 4 files changed, 32 insertions(+) create mode 100644 tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Ambiguous/other/A.php diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 73c036a37..d470ca2da 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -79,6 +79,10 @@ class ClassMapGenerator $classes = self::findClasses($filePath); foreach ($classes as $class) { + if (array_key_exists($class, $map)) { + throw new \RuntimeException('Ambiguous reference to class "'.$class.'" was found.'); + } + $map[$class] = $filePath; } } diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index ddadc89c5..fe33b1f36 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -104,6 +104,22 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $find->invoke(null, __DIR__.'/no-file'); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Ambiguous reference to class "A" was found. + */ + public function testAmbiguousReference() + { + if (!class_exists('Symfony\\Component\\Finder\\Finder')) { + $this->markTestSkipped('Finder component is not available'); + } + + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); + + ClassMapGenerator::createMap($finder); + } + /** * @expectedException \RuntimeException * @expectedExceptionMessage Could not scan for classes inside diff --git a/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php b/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php new file mode 100644 index 000000000..ee9325efa --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php @@ -0,0 +1,6 @@ + Date: Thu, 20 Mar 2014 14:24:42 +0100 Subject: [PATCH 160/638] improve exception message --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- tests/Composer/Test/Autoload/ClassMapGeneratorTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index d470ca2da..77dc08dc9 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -80,7 +80,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (array_key_exists($class, $map)) { - throw new \RuntimeException('Ambiguous reference to class "'.$class.'" was found.'); + throw new \RuntimeException('Ambiguous class "'.$class.'" resolution; defined in "'.$map[$class].'" and in "'.$filePath.'" file.'); } $map[$class] = $filePath; diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index fe33b1f36..58e68c0d5 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -106,7 +106,6 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \RuntimeException - * @expectedExceptionMessage Ambiguous reference to class "A" was found. */ public function testAmbiguousReference() { From a41370b021615bece3aa5ec3de669712f112dc15 Mon Sep 17 00:00:00 2001 From: Jan Brecka Date: Fri, 21 Mar 2014 14:26:54 +0100 Subject: [PATCH 161/638] fix plural in exception message --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 77dc08dc9..5c2859271 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -80,7 +80,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (array_key_exists($class, $map)) { - throw new \RuntimeException('Ambiguous class "'.$class.'" resolution; defined in "'.$map[$class].'" and in "'.$filePath.'" file.'); + throw new \RuntimeException('Ambiguous class "'.$class.'" resolution; defined in "'.$map[$class].'" and in "'.$filePath.'" files.'); } $map[$class] = $filePath; From 15b8c6f1c38b6c2541616b44b00928526f00b7db Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Fri, 21 Mar 2014 13:42:00 -0500 Subject: [PATCH 162/638] Fixing perforce dev-master stored reference bug. --- src/Composer/Util/Perforce.php | 17 ++++++++-------- tests/Composer/Test/Util/PerforceTest.php | 24 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index f3e9d2023..ae6f44d04 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -315,11 +315,7 @@ class Perforce chdir($this->path); $p4SyncCommand = $this->generateP4Command('sync -f '); - if (isset($label)) { - if (strcmp($label, 'dev-master') != 0) { - $p4SyncCommand = $p4SyncCommand . '@' . $label; - } - } + $p4SyncCommand = $p4SyncCommand . '@' . $label; $this->executeCommand($p4SyncCommand); chdir($prevDir); @@ -481,9 +477,15 @@ class Perforce } } } - $branches = array(); - $branches['master'] = $possibleBranches[$this->p4Branch]; + $command = $this->generateP4Command('changes '. $this->getStream() . '/...', false); + $this->executeCommand($command); + $result = $this->commandResult; + $resArray = explode(PHP_EOL, $result); + $lastCommit = $resArray[0]; + $lastCommitArr = explode(' ', $lastCommit); + $lastCommitNum = $lastCommitArr[1]; + $branches = array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum); return $branches; } @@ -501,7 +503,6 @@ class Perforce $tags[$fields[1]] = $this->getStream() . '@' . $fields[1]; } } - return $tags; } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 06e3cce8b..1b8063258 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -351,15 +351,35 @@ class PerforceTest extends \PHPUnit_Framework_TestCase } ) ); + $expectedCommand2 = 'p4 -u user -p port changes //depot/branch/...'; + $expectedCallback = function($command, &$output) + { + $output = 'Change 1234 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; + }; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand2)) + ->will($this->returnCallback($expectedCallback)); $branches = $this->perforce->getBranches(); - $this->assertEquals('//depot/branch', $branches['master']); + $this->assertEquals('//depot/branch@1234', $branches['master']); } public function testGetBranchesWithoutStream() { + $expectedCommand = 'p4 -u user -p port changes //depot/...'; + $expectedCallback = function($command, &$output) + { + $output = 'Change 5678 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; + }; + $this->processExecutor->expects($this->once()) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($expectedCallback)); $branches = $this->perforce->getBranches(); - $this->assertEquals('//depot', $branches['master']); + $this->assertEquals('//depot@5678', $branches['master']); } public function testGetTagsWithoutStream() From 0f7b078d6ccb555348ade0abe1a854e9bd53e841 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Fri, 14 Mar 2014 14:45:31 -0500 Subject: [PATCH 163/638] added new dependency to Perforce object, updating some tests. --- .../Downloader/PerforceDownloader.php | 4 +- .../Repository/Vcs/PerforceDriver.php | 2 +- src/Composer/Util/Perforce.php | 21 ++- .../Downloader/PerforceDownloaderTest.php | 160 +++++++++++------- tests/Composer/Test/Util/PerforceTest.php | 11 +- 5 files changed, 122 insertions(+), 76 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 2bb1ba619..632ac648c 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -22,7 +22,7 @@ use Composer\Util\Perforce; class PerforceDownloader extends VcsDownloader { protected $perforce; - protected $perforceInjected = false; +// protected $perforceInjected = false; /** * {@inheritDoc} @@ -54,7 +54,7 @@ class PerforceDownloader extends VcsDownloader if ($repository instanceof VcsRepository) { $repoConfig = $this->getRepoConfig($repository); } - $this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path); + $this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path, $this->process, $this->io); } private function getRepoConfig(VcsRepository $repository) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 79500f1d6..8a1b40623 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -56,7 +56,7 @@ class PerforceDriver extends VcsDriver } $repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot; - $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process); + $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io); } /** diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 94b9730eb..c3b66329f 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -35,27 +35,36 @@ class Perforce protected $windowsFlag; protected $commandResult; - public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows) + protected $io; + + public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows, IOInterface $io) { $this->windowsFlag = $isWindows; $this->p4Port = $port; $this->initializePath($path); $this->process = $process; $this->initialize($repoConfig); + $this->io = $io; } - public static function create($repoConfig, $port, $path, ProcessExecutor $process = null) + public static function create($repoConfig, $port, $path, ProcessExecutor $process = null, IOInterface $io) { if (!isset($process)) { $process = new ProcessExecutor; } $isWindows = defined('PHP_WINDOWS_VERSION_BUILD'); - $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows); + $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io); return $perforce; } + public static function checkServerExists($url, ProcessExecutor $processExecutor) + { + $output = null; + return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output); + } + public function initialize($repoConfig) { $this->uniquePerforceClientName = $this->generateUniquePerforceClientName(); @@ -382,12 +391,6 @@ class Perforce } } - public static function checkServerExists($url, ProcessExecutor $processExecutor) - { - $output = null; - return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output); - } - public function getComposerInformation($identifier) { $index = strpos($identifier, '@'); diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 8a20f67cc..0eb981362 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -15,86 +15,120 @@ namespace Composer\Test\Downloader; use Composer\Downloader\PerforceDownloader; use Composer\Config; use Composer\Repository\VcsRepository; +use Composer\IO\IOInterface; /** * @author Matt Whittom */ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase { - private $io; - private $config; - private $testPath; - public static $repository; + + protected $config; + protected $downloader; + protected $io; + protected $package; + protected $processExecutor; + protected $repoConfig; + protected $repository; + protected $testPath; - protected function setUp() + public function setUp() { - $this->testPath = sys_get_temp_dir() . '/composer-test'; - $this->config = new Config(); - $this->config->merge( - array( - 'config' => array( - 'home' => $this->testPath, - ), - ) - ); - $this->io = $this->getMock('Composer\IO\IOInterface'); + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->repoConfig = $this->getRepoConfig(); + $this->config = $this->getConfig(); + $this->io = $this->getMockIoInterface(); + $this->processExecutor = $this->getMockProcessExecutor(); + $this->repository = $this->getMockRepository($this->repoConfig, $this->io, $this->config); + $this->package = $this->getMockPackageInterface($this->repository); + $this->downloader = new PerforceDownloader($this->io, $this->config, $this->processExecutor); } - public function testInitPerforceGetRepoConfig() + public function tearDown() + { + $this->downloader = null; + $this->package = null; + $this->repository = null; + $this->io = null; + $this->config = null; + $this->repoConfig = null; + $this->testPath = null; + } + + protected function getMockProcessExecutor() + { + return $this->getMock('Composer\Util\ProcessExecutor'); + } + + protected function getConfig() + { + $config = new Config(); + $settings = array('config' => array('home' => $this->testPath)); + $config->merge($settings); + return $config; + } + + protected function getMockIoInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + + protected function getMockPackageInterface(VcsRepository $repository) { - $downloader = new PerforceDownloader($this->io, $this->config); $package = $this->getMock('Composer\Package\PackageInterface'); - $repoConfig = array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); - $repository = $this->getMock( - 'Composer\Repository\VcsRepository', - array('getRepoConfig'), - array($repoConfig, $this->io, $this->config) - ); - $package->expects($this->at(0)) - ->method('getRepository') - ->will($this->returnValue($repository)); - $repository->expects($this->at(0)) - ->method('getRepoConfig'); - $path = $this->testPath; - $downloader->initPerforce($package, $path, 'SOURCE_REF'); + $package->expects($this->any())->method('getRepository')->will($this->returnValue($repository)); + return $package; } + protected function getRepoConfig() + { + return array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); + } + + protected function getMockRepository(array $repoConfig, IOInterface $io, Config $config) + { + $class = 'Composer\Repository\VcsRepository'; + $methods = array('getRepoConfig'); + $args = array($repoConfig, $io, $config); + $repository = $this->getMock($class, $methods, $args); + $repository->expects($this->any())->method('getRepoConfig')->will($this->returnValue($repoConfig)); + return $repository; + } + + public function testInitPerforceInstantiatesANewPerforceObject() + { + $this->downloader->initPerforce($this->package, $this->testPath, 'SOURCE_REF'); + } + + public function testInitPerforceDoesNothingIfPerforceAlreadySet() + { + $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock(); + $this->downloader->setPerforce($perforce); + $this->repository->expects($this->never())->method('getRepoConfig'); + $this->downloader->initPerforce($this->package, $this->testPath, 'SOURCE_REF'); + } + + /** + * @depends testInitPerforceInstantiatesANewPerforceObject + * @depends testInitPerforceDoesNothingIfPerforceAlreadySet + */ public function testDoDownload() { - $downloader = new PerforceDownloader($this->io, $this->config); - $repoConfig = array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH', 'p4user' => 'TEST_USER'); - $port = 'TEST_PORT'; - $path = 'TEST_PATH'; - $process = $this->getmock('Composer\Util\ProcessExecutor'); - $perforce = $this->getMock( - 'Composer\Util\Perforce', - array('setStream', 'queryP4User', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase'), - array($repoConfig, $port, $path, $process, true, 'TEST') - ); + //I really don't like this test but the logic of each Perforce method is tested in the Perforce class. Really I am just enforcing workflow. $ref = 'SOURCE_REF'; $label = 'LABEL'; - $perforce->expects($this->at(0)) - ->method('setStream') - ->with($this->equalTo($ref)); - $perforce->expects($this->at(1)) - ->method('queryP4User') - ->with($this->io); - $perforce->expects($this->at(2)) - ->method('writeP4ClientSpec'); - $perforce->expects($this->at(3)) - ->method('connectClient'); - $perforce->expects($this->at(4)) - ->method('syncCodeBase') - ->with($this->equalTo($label)); - $downloader->setPerforce($perforce); - $package = $this->getMock('Composer\Package\PackageInterface'); - $package->expects($this->at(0)) - ->method('getSourceReference') - ->will($this->returnValue($ref)); - $package->expects($this->at(1)) - ->method('getPrettyVersion') - ->will($this->returnValue($label)); - $path = $this->testPath; - $downloader->doDownload($package, $path); + $this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref)); + $this->package->expects($this->once())->method('getPrettyVersion')->will($this->returnValue($label)); + $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); + $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); + $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); + $perforce->expects($this->at(0))->method('setStream')->with($this->equalTo($ref)); + $perforce->expects($this->at(1))->method('p4Login')->with($this->identicalTo($this->io)); + $perforce->expects($this->at(2))->method('writeP4ClientSpec'); + $perforce->expects($this->at(3))->method('connectClient'); + $perforce->expects($this->at(4))->method('syncCodeBase'); + $perforce->expects($this->at(5))->method('cleanupClientSpec'); + $this->downloader->setPerforce($perforce); + $this->downloader->doDownload($this->package, $this->testPath); } } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index f2eeb1964..56a234536 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -22,6 +22,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { protected $perforce; protected $processExecutor; + protected $io; public function setUp() { @@ -32,7 +33,15 @@ class PerforceTest extends \PHPUnit_Framework_TestCase 'p4user' => 'user', 'unique_perforce_client_name' => 'TEST' ); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true); + $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, $io); + } + + public function tearDown() + { + $this->perforce = null; + $this->io = null; + $this->processExecutor = null; } public function testGetClientWithoutStream() From 24dd42267f391f49ee311672f9d93b009d21239b Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Mon, 17 Mar 2014 16:06:19 -0500 Subject: [PATCH 164/638] almost all unit tests passing after IOInterface dependency refactor. no longer passing IOInterface into any Perforce methods --- .../Downloader/PerforceDownloader.php | 2 +- .../Repository/Vcs/PerforceDriver.php | 8 +- src/Composer/Util/Perforce.php | 29 ++- .../Downloader/PerforceDownloaderTest.php | 13 +- .../Repository/Vcs/PerforceDriverTest.php | 186 +++++++------- tests/Composer/Test/Util/PerforceTest.php | 230 ++++++++---------- 6 files changed, 239 insertions(+), 229 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 632ac648c..da70c3469 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -44,7 +44,7 @@ class PerforceDownloader extends VcsDownloader public function initPerforce($package, $path) { - if ($this->perforce) { + if (!empty($this->perforce)) { $this->perforce->initializePath($path); return; } diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 8a1b40623..6a55033a1 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -35,7 +35,7 @@ class PerforceDriver extends VcsDriver { $this->depot = $this->repoConfig['depot']; $this->branch = ''; - if (isset($this->repoConfig['branch'])) { + if (!empty($this->repoConfig['branch'])) { $this->branch = $this->repoConfig['branch']; } @@ -51,7 +51,7 @@ class PerforceDriver extends VcsDriver private function initPerforce($repoConfig) { - if (isset($this->perforce)) { + if (!empty($this->perforce)) { return; } @@ -64,7 +64,7 @@ class PerforceDriver extends VcsDriver */ public function getComposerInformation($identifier) { - if (isset($this->composerInfoIdentifier)) { + if (!empty($this->composerInfoIdentifier)) { if (strcmp($identifier, $this->composerInfoIdentifier) === 0) { return $this->composerInfo; } @@ -141,7 +141,7 @@ class PerforceDriver extends VcsDriver $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; $result = false; - if (isset($this->composerInfo)) { + if (!empty($this->composerInfo)) { $result = count($this->composerInfo) > 0; } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index c3b66329f..2a1094e59 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -200,7 +200,12 @@ class Perforce return $this->p4User; } - public function queryP4User(IOInterface $io) + public function setUser($user) + { + $this->p4User = $user; + } + + public function queryP4User() { $this->getUser(); if (strlen($this->p4User) > 0) { @@ -210,7 +215,7 @@ class Perforce if (strlen($this->p4User) > 0) { return; } - $this->p4User = $io->ask('Enter P4 User:'); + $this->p4User = $this->io->ask('Enter P4 User:'); if ($this->windowsFlag) { $command = 'p4 set P4USER=' . $this->p4User; } else { @@ -248,14 +253,14 @@ class Perforce } } - public function queryP4Password(IOInterface $io) + public function queryP4Password() { if (isset($this->p4Password)) { return $this->p4Password; } $password = $this->getP4variable('P4PASSWD'); if (strlen($password) <= 0) { - $password = $io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); + $password = $this->io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); } $this->p4Password = $password; @@ -365,6 +370,16 @@ class Perforce return; } + public function getWindowsFlag() + { + return $this->windowsFlag; + } + + public function setWindowsFlag($flag) + { + $this->windowsFlag = $flag; + } + public function windowsLogin($password) { $command = $this->generateP4Command(' login -a'); @@ -373,11 +388,11 @@ class Perforce return $process->run(); } - public function p4Login(IOInterface $io) + public function p4Login() { - $this->queryP4User($io); + $this->queryP4User(); if (!$this->isLoggedIn()) { - $password = $this->queryP4Password($io); + $password = $this->queryP4Password(); if ($this->windowsFlag) { $this->windowsLogin($password); } else { diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 0eb981362..c78f1ed13 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -122,12 +122,13 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); - $perforce->expects($this->at(0))->method('setStream')->with($this->equalTo($ref)); - $perforce->expects($this->at(1))->method('p4Login')->with($this->identicalTo($this->io)); - $perforce->expects($this->at(2))->method('writeP4ClientSpec'); - $perforce->expects($this->at(3))->method('connectClient'); - $perforce->expects($this->at(4))->method('syncCodeBase'); - $perforce->expects($this->at(5))->method('cleanupClientSpec'); + $perforce->expects($this->at(0))->method('initializePath')->with($this->equalTo($this->testPath)); + $perforce->expects($this->at(1))->method('setStream')->with($this->equalTo($ref)); + $perforce->expects($this->at(2))->method('p4Login')->with($this->identicalTo($this->io)); + $perforce->expects($this->at(3))->method('writeP4ClientSpec'); + $perforce->expects($this->at(4))->method('connectClient'); + $perforce->expects($this->at(5))->method('syncCodeBase'); + $perforce->expects($this->at(6))->method('cleanupClientSpec'); $this->downloader->setPerforce($perforce); $this->downloader->doDownload($this->package, $this->testPath); } diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 36cd69ebc..bc228de7e 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -21,113 +21,129 @@ use Composer\Config; */ class PerforceDriverTest extends \PHPUnit_Framework_TestCase { - private $config; - private $io; - private $process; - private $remoteFileSystem; - private $testPath; + protected $config; + protected $io; + protected $process; + protected $remoteFileSystem; + protected $testPath; + protected $driver; + protected $repoConfig; + + const TEST_URL = 'TEST_PERFORCE_URL'; + const TEST_DEPOT = 'TEST_DEPOT_CONFIG'; + const TEST_BRANCH = 'TEST_BRANCH_CONFIG'; public function setUp() { - $this->testPath = sys_get_temp_dir() . '/composer-test'; - $this->config = new Config(); - $this->config->merge( - array( - 'config' => array( - 'home' => $this->testPath, - ), - ) - ); - - $this->io = $this->getMock('Composer\IO\IOInterface'); - $this->process = $this->getMock('Composer\Util\ProcessExecutor'); - $this->remoteFileSystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor() - ->getMock(); + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->config = $this->getTestConfig($this->testPath); + $this->repoConfig = $this->getTestRepoConfig(); + $this->io = $this->getMockIOInterface(); + $this->process = $this->getMockProcessExecutor(); + $this->remoteFileSystem = $this->getMockRemoteFilesystem(); + $this->perforce = $this->getMockPerforce(); + $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); } public function tearDown() { + //cleanup directory under test path $fs = new Filesystem; $fs->removeDirectory($this->testPath); + $this->driver = null; + $this->perforce = null; + $this->remoteFileSystem = null; + $this->process = null; + $this->io = null; + $this->repoConfig = null; + $this->config = null; + $this->testPath = null; + } + + protected function getTestConfig($testPath) + { + $config = new Config(); + $config->merge(array('config'=>array('home'=>$testPath))); + return $config; + } + + protected function getTestRepoConfig() + { + return array( + 'url' => self::TEST_URL, + 'depot' => self::TEST_DEPOT, + 'branch' => self::TEST_BRANCH, + ); + } + + protected function getMockIOInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + + protected function getMockProcessExecutor() + { + return $this->getMock('Composer\Util\ProcessExecutor'); + } + + protected function getMockRemoteFilesystem() + { + return $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock(); + } + + protected function getMockPerforce() + { + $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation'); + return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock(); } public function testInitializeCapturesVariablesFromRepoConfig() { - $this->setUp(); - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $process = $this->getMock('Composer\Util\ProcessExecutor'); - $arguments = array( - array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), - 'port' => 'TEST_PORT', - 'path' => $this->testPath, - $process, - true, - 'TEST' - ); - $perforce = $this->getMock('Composer\Util\Perforce', null, $arguments); - $driver->setPerforce($perforce); + $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $driver->setPerforce($this->perforce); $driver->initialize(); - $this->assertEquals('TEST_PERFORCE_URL', $driver->getUrl()); - $this->assertEquals('TEST_DEPOT_CONFIG', $driver->getDepot()); - $this->assertEquals('TEST_BRANCH_CONFIG', $driver->getBranch()); + $this->assertEquals(self::TEST_URL, $driver->getUrl()); + $this->assertEquals(self::TEST_DEPOT, $driver->getDepot()); + $this->assertEquals(self::TEST_BRANCH, $driver->getBranch()); } public function testInitializeLogsInAndConnectsClient() { - $this->setUp(); - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock(); - $perforce->expects($this->at(0)) - ->method('p4Login') - ->with($this->io); - $perforce->expects($this->at(1)) - ->method('checkStream') - ->with($this->equalTo('TEST_DEPOT_CONFIG')); - $perforce->expects($this->at(2)) - ->method('writeP4ClientSpec'); - $perforce->expects($this->at(3)) - ->method('connectClient'); - - $driver->setPerforce($perforce); - $driver->initialize(); + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('p4Login')->with($this->identicalTo($this->io)); + $this->perforce->expects($this->at(1))->method('checkStream')->with($this->equalTo(self::TEST_DEPOT)); + $this->perforce->expects($this->at(2))->method('writeP4ClientSpec'); + $this->perforce->expects($this->at(3))->method('connectClient'); + $this->driver->initialize(); } - public function testHasComposerFile() + /** + * @depends testInitializeCapturesVariablesFromRepoConfig + * @depends testInitializeLogsInAndConnectsClient + */ + public function testHasComposerFileReturnsFalseOnNoComposerFile() { - $repoConfig = array( - 'url' => 'TEST_PERFORCE_URL', - 'depot' => 'TEST_DEPOT_CONFIG', - 'branch' => 'TEST_BRANCH_CONFIG' - ); - $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $process = $this->getMock('Composer\Util\ProcessExecutor'); - $arguments = array( - array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), - 'port' => 'TEST_PORT', - 'path' => $this->testPath, - $process, - true, - 'TEST' - ); - $perforce = $this->getMock('Composer\Util\Perforce', array('getComposerInformation'), $arguments); - $perforce->expects($this->at(0)) - ->method('getComposerInformation') - ->with($this->equalTo('//TEST_DEPOT_CONFIG/TEST_IDENTIFIER')) - ->will($this->returnValue('Some json stuff')); - $driver->setPerforce($perforce); - $driver->initialize(); $identifier = 'TEST_IDENTIFIER'; - $result = $driver->hasComposerFile($identifier); + $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); + $this->driver->initialize(); + $result = $this->driver->hasComposerFile($identifier); + $this->assertFalse($result); + } + + /** + * @depends testInitializeCapturesVariablesFromRepoConfig + * @depends testInitializeLogsInAndConnectsClient + */ + public function testHasComposerFileReturnsTrueWithOneOrMoreComposerFiles() + { + $identifier = 'TEST_IDENTIFIER'; + $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; + $this->driver->setPerforce($this->perforce); + $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); + $this->driver->initialize(); + $result = $this->driver->hasComposerFile($identifier); $this->assertTrue($result); } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 56a234536..b7865d5e6 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -24,26 +24,44 @@ class PerforceTest extends \PHPUnit_Framework_TestCase protected $processExecutor; protected $io; + const TEST_DEPOT = 'depot'; + const TEST_BRANCH = 'branch'; + const TEST_P4USER = 'user'; + const TEST_CLIENT_NAME = 'TEST'; + const TEST_PORT = 'port'; + const TEST_PATH = 'path'; + public function setUp() { $this->processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $repoConfig = array( - 'depot' => 'depot', - 'branch' => 'branch', - 'p4user' => 'user', - 'unique_perforce_client_name' => 'TEST' - ); - $io = $this->getMock('Composer\IO\IOInterface'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, $io); + $this->repoConfig = $this->getTestRepoConfig(); + $this->io = $this->getMockIOInterface(); + $this->perforce = new Perforce($this->repoConfig, self::TEST_PORT, self::TEST_PATH, $this->processExecutor, true, $this->io); } public function tearDown() { - $this->perforce = null; - $this->io = null; + $this->perforce = null; + $this->io = null; + $this->repoConfig = null; $this->processExecutor = null; } + public function getTestRepoConfig() + { + return array( + 'depot' => self::TEST_DEPOT, + 'branch' => self::TEST_BRANCH, + 'p4user' => self::TEST_P4USER, + 'unique_perforce_client_name' => self::TEST_CLIENT_NAME + ); + } + + public function getMockIOInterface() + { + return $this->getMock('Composer\IO\IOInterface'); + } + public function testGetClientWithoutStream() { $client = $this->perforce->getClient(); @@ -107,116 +125,90 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserWithUserAlreadySet() { - $io = $this->getMock('Composer\IO\IOInterface'); - - $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'TEST_USER'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $this->perforce->queryP4user($io); - $this->assertEquals('TEST_USER', $this->perforce->getUser()); + $this->perforce->queryP4user(); + $this->assertEquals(self::TEST_P4USER, $this->perforce->getUser()); } public function testQueryP4UserWithUserSetInP4VariablesWithWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(true); $expectedCommand = 'p4 set'; + $callback = function($command, &$output) + { + $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL ; - - return true; - } - ) - ); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $this->perforce->queryP4user(); $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); } public function testQueryP4UserWithUserSetInP4VariablesNotWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(false); $expectedCommand = 'echo $P4USER'; + $callback = function($command, &$output) + { + $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; - - return true; - } - ) - ); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $this->perforce->queryP4user(); $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); } public function testQueryP4UserQueriesForUser() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - - $this->perforce->queryP4user($io); + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); + $this->perforce->queryP4user(); $this->assertEquals('TEST_QUERY_USER', $this->perforce->getUser()); } public function testQueryP4UserStoresResponseToQueryForUserWithWindows() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(true); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; + $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); $this->processExecutor->expects($this->at(1)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will($this->returnValue(0)); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + $this->perforce->queryP4user(); } public function testQueryP4UserStoresResponseToQueryForUserWithoutWindows() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setUser(null); + $this->perforce->setWindowsFlag(false); $expectedQuestion = 'Enter P4 User:'; - $io->expects($this->at(0)) - ->method('ask') - ->with($this->equalTo($expectedQuestion)) - ->will($this->returnValue('TEST_QUERY_USER')); - $expectedCommand = 'export P4USER=TEST_QUERY_USER'; + $expectedCommand = 'export P4USER=TEST_QUERY_USER'; + $this->io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); $this->processExecutor->expects($this->at(1)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will($this->returnValue(0)); - - $this->perforce->queryP4user($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + $this->perforce->queryP4user(); } public function testQueryP4PasswordWithPasswordAlreadySet() @@ -227,69 +219,55 @@ class PerforceTest extends \PHPUnit_Framework_TestCase 'p4user' => 'user', 'p4password' => 'TEST_PASSWORD' ); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - $io = $this->getMock('Composer\IO\IOInterface'); - - $password = $this->perforce->queryP4Password($io); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, $this->getMockIOInterface(), 'TEST'); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_PASSWORD', $password); } public function testQueryP4PasswordWithPasswordSetInP4VariablesWithWindowsOS() { - $io = $this->getMock('Composer\IO\IOInterface'); - + $this->perforce->setWindowsFlag(true); $expectedCommand = 'p4 set'; + $callback = function($command, &$output) + { + $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; - - return true; - } - ) - ); - - $password = $this->perforce->queryP4Password($io); + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); } public function testQueryP4PasswordWithPasswordSetInP4VariablesNotWindowsOS() { - $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'user'); - $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); - - $io = $this->getMock('Composer\IO\IOInterface'); + $this->perforce->setWindowsFlag(false); $expectedCommand = 'echo $P4PASSWD'; + $callback = function($command, &$output) + { + $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + }; $this->processExecutor->expects($this->at(0)) - ->method('execute') - ->with($this->equalTo($expectedCommand)) - ->will( - $this->returnCallback( - function ($command, &$output) { - $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($callback)); - return true; - } - ) - ); - - $password = $this->perforce->queryP4Password($io); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); } public function testQueryP4PasswordQueriesForPassword() { - $io = $this->getMock('Composer\IO\IOInterface'); $expectedQuestion = 'Enter password for Perforce user user: '; - $io->expects($this->at(0)) + $this->io->expects($this->at(0)) ->method('askAndHideAnswer') ->with($this->equalTo($expectedQuestion)) ->will($this->returnValue('TEST_QUERY_PASSWORD')); - $password = $this->perforce->queryP4Password($io); + $password = $this->perforce->queryP4Password(); $this->assertEquals('TEST_QUERY_PASSWORD', $password); } From 492539101cf660f394545bbde32152c29ec1d965 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 18 Mar 2014 13:52:54 -0500 Subject: [PATCH 165/638] got all unit tests passing --- src/Composer/Repository/Vcs/PerforceDriver.php | 2 ++ src/Composer/Util/Perforce.php | 2 +- tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 6a55033a1..535a79516 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -141,6 +141,8 @@ class PerforceDriver extends VcsDriver $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; $result = false; + return !empty($this->composerInfo); + if (!empty($this->composerInfo)) { $result = count($this->composerInfo) > 0; } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 2a1094e59..ecafaf6f0 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -47,7 +47,7 @@ class Perforce $this->io = $io; } - public static function create($repoConfig, $port, $path, ProcessExecutor $process = null, IOInterface $io) + public static function create($repoConfig, $port, $path, ProcessExecutor $process, IOInterface $io) { if (!isset($process)) { $process = new ProcessExecutor; diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index bc228de7e..7666efebc 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -126,7 +126,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; $this->driver->setPerforce($this->perforce); - $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); + $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); $this->assertFalse($result); @@ -141,7 +141,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; $this->driver->setPerforce($this->perforce); - $this->perforce->expects($this->at(0))->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); + $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); $this->assertTrue($result); From c11105dd6033f25225ce2c16baf64465f891e2a8 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 18 Mar 2014 14:39:47 -0500 Subject: [PATCH 166/638] Fixing bug not cleaning up workspaces. --- .../Repository/Vcs/PerforceDriver.php | 6 +++++ src/Composer/Util/Perforce.php | 24 ++++++++++++++++--- .../Repository/Vcs/PerforceDriverTest.php | 11 ++++++++- tests/Composer/Test/Util/PerforceTest.php | 15 ++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 535a79516..090de1bb8 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -193,4 +193,10 @@ class PerforceDriver extends VcsDriver { $this->perforce = $perforce; } + + public function getPerforce() + { + return $this->perforce; + } + } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index ecafaf6f0..659cbfc67 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -37,6 +37,8 @@ class Perforce protected $io; + protected $filesystem; + public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows, IOInterface $io) { $this->windowsFlag = $isWindows; @@ -109,10 +111,10 @@ class Perforce public function cleanupClientSpec() { $client = $this->getClient(); - $command = 'p4 client -d $client'; + $command = 'p4 client -d ' . $client; $this->executeCommand($command); $clientSpec = $this->getP4ClientSpec(); - $fileSystem = new FileSystem($this->process); + $fileSystem = $this->getFilesystem(); $fileSystem->remove($clientSpec); } @@ -141,7 +143,7 @@ class Perforce public function initializePath($path) { $this->path = $path; - $fs = new Filesystem(); + $fs = $this->getFilesystem(); $fs->ensureDirectoryExists($path); } @@ -559,4 +561,20 @@ class Perforce return $result; } + + public function getFilesystem() + { + if (empty($this->filesystem)) + { + $this->filesystem = new Filesystem($this->process); + } + return $this->filesystem; + } + + + public function setFilesystem(Filesystem $fs) + { + $this->filesystem = $fs; + } + } diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 7666efebc..9afc57d30 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -93,7 +93,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase protected function getMockPerforce() { - $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation'); + $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation', 'cleanupClientSpec'); return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock(); } @@ -159,4 +159,13 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->expectOutputString(''); $this->assertFalse(PerforceDriver::supports($this->io, $this->config, 'existing.url')); } + + public function testCleanup() + { + $this->perforce->expects($this->once())->method('cleanupClientSpec'); + $this->driver->setPerforce($this->perforce); + $this->driver->cleanup(); + $this->assertNull($this->driver->getPerforce()); + } + } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index b7865d5e6..65857343b 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -679,4 +679,19 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { $this->perforce->setStream('//depot/branch'); } + + public function testCleanupClientSpecShouldDeleteClient() + { + $fs = $this->getMock('Composer\Util\Filesystem'); + $this->perforce->setFilesystem($fs); + + $testClient = $this->perforce->getClient(); + $expectedCommand = 'p4 client -d ' . $testClient; + $this->processExecutor->expects($this->once())->method('execute')->with($this->equalTo($expectedCommand)); + + $fs->expects($this->once())->method('remove')->with($this->perforce->getP4ClientSpec()); + + $this->perforce->cleanupClientSpec(); + } + } From 8fc1961463e4ff549a7e949259dedee2a86bdf64 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Wed, 19 Mar 2014 13:58:41 -0500 Subject: [PATCH 167/638] Fixing delete client workspace bug. --- src/Composer/Util/Perforce.php | 4 +++- tests/Composer/Test/Util/PerforceTest.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 659cbfc67..f3e9d2023 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -111,7 +111,9 @@ class Perforce public function cleanupClientSpec() { $client = $this->getClient(); - $command = 'p4 client -d ' . $client; + $task = 'client -d ' . $client; + $useP4Client = false; + $command = $this->generateP4Command($task, $useP4Client); $this->executeCommand($command); $clientSpec = $this->getP4ClientSpec(); $fileSystem = $this->getFilesystem(); diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 65857343b..06e3cce8b 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -686,7 +686,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $this->perforce->setFilesystem($fs); $testClient = $this->perforce->getClient(); - $expectedCommand = 'p4 client -d ' . $testClient; + $expectedCommand = 'p4 -u ' . self::TEST_P4USER . ' -p ' . self::TEST_PORT . ' client -d ' . $testClient; $this->processExecutor->expects($this->once())->method('execute')->with($this->equalTo($expectedCommand)); $fs->expects($this->once())->method('remove')->with($this->perforce->getP4ClientSpec()); From 2651cbc5fe573a61d0dec106f8c48e6187ba05fc Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Fri, 21 Mar 2014 13:42:00 -0500 Subject: [PATCH 168/638] Fixing perforce dev-master stored reference bug. --- src/Composer/Util/Perforce.php | 17 ++++++++-------- tests/Composer/Test/Util/PerforceTest.php | 24 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index f3e9d2023..ae6f44d04 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -315,11 +315,7 @@ class Perforce chdir($this->path); $p4SyncCommand = $this->generateP4Command('sync -f '); - if (isset($label)) { - if (strcmp($label, 'dev-master') != 0) { - $p4SyncCommand = $p4SyncCommand . '@' . $label; - } - } + $p4SyncCommand = $p4SyncCommand . '@' . $label; $this->executeCommand($p4SyncCommand); chdir($prevDir); @@ -481,9 +477,15 @@ class Perforce } } } - $branches = array(); - $branches['master'] = $possibleBranches[$this->p4Branch]; + $command = $this->generateP4Command('changes '. $this->getStream() . '/...', false); + $this->executeCommand($command); + $result = $this->commandResult; + $resArray = explode(PHP_EOL, $result); + $lastCommit = $resArray[0]; + $lastCommitArr = explode(' ', $lastCommit); + $lastCommitNum = $lastCommitArr[1]; + $branches = array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum); return $branches; } @@ -501,7 +503,6 @@ class Perforce $tags[$fields[1]] = $this->getStream() . '@' . $fields[1]; } } - return $tags; } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 06e3cce8b..1b8063258 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -351,15 +351,35 @@ class PerforceTest extends \PHPUnit_Framework_TestCase } ) ); + $expectedCommand2 = 'p4 -u user -p port changes //depot/branch/...'; + $expectedCallback = function($command, &$output) + { + $output = 'Change 1234 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; + }; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand2)) + ->will($this->returnCallback($expectedCallback)); $branches = $this->perforce->getBranches(); - $this->assertEquals('//depot/branch', $branches['master']); + $this->assertEquals('//depot/branch@1234', $branches['master']); } public function testGetBranchesWithoutStream() { + $expectedCommand = 'p4 -u user -p port changes //depot/...'; + $expectedCallback = function($command, &$output) + { + $output = 'Change 5678 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; + }; + $this->processExecutor->expects($this->once()) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnCallback($expectedCallback)); $branches = $this->perforce->getBranches(); - $this->assertEquals('//depot', $branches['master']); + $this->assertEquals('//depot@5678', $branches['master']); } public function testGetTagsWithoutStream() From fd4fb80d1eb6ecf7583657b64e09add1b7292746 Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Tue, 18 Feb 2014 15:48:45 +0100 Subject: [PATCH 169/638] Fix for #2557, added missing run-scripts --- src/Composer/Command/RunScriptCommand.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index c4a3a3563..81b599858 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -52,6 +52,12 @@ EOT ScriptEvents::POST_UPDATE_CMD, ScriptEvents::PRE_STATUS_CMD, ScriptEvents::POST_STATUS_CMD, + ScriptEvents::POST_ROOT_PACKAGE_INSTALL, + ScriptEvents::POST_CREATE_PROJECT_CMD, + ScriptEvents::PRE_ARCHIVE_CMD, + ScriptEvents::POST_ARCHIVE_CMD, + ScriptEvents::PRE_AUTOLOAD_DUMP, + ScriptEvents::POST_AUTOLOAD_DUMP ))) { if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script)); From 2618e9a4f128eee6006e3ad20eaf92629187a9af Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Wed, 19 Feb 2014 13:37:38 +0100 Subject: [PATCH 170/638] Fixed some docblocks --- src/Composer/Composer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 0d1e0aa89..6731ea9a4 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -67,7 +67,7 @@ class Composer private $config; /** - * @var EventDispatcher\EventDispatcher + * @var EventDispatcher */ private $eventDispatcher; @@ -190,7 +190,7 @@ class Composer } /** - * @param EventDispatcher\EventDispatcher $eventDispatcher + * @param EventDispatcher $eventDispatcher */ public function setEventDispatcher(EventDispatcher $eventDispatcher) { @@ -198,7 +198,7 @@ class Composer } /** - * @return EventDispatcher\EventDispatcher + * @return EventDispatcher */ public function getEventDispatcher() { From b9efdd8348be7518b9b7404774936c402f030c3a Mon Sep 17 00:00:00 2001 From: Sandy Pleyte Date: Wed, 19 Feb 2014 13:38:51 +0100 Subject: [PATCH 171/638] Separated the scripts events in 2 arrays because they need to be called by different dispatchers. --- src/Composer/Command/RunScriptCommand.php | 45 +++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 81b599858..0ccf868ad 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -23,6 +23,30 @@ use Symfony\Component\Console\Output\OutputInterface; */ class RunScriptCommand extends Command { + /** + * @var array Array with command events + */ + protected $commandEvents = array( + ScriptEvents::PRE_INSTALL_CMD, + ScriptEvents::POST_INSTALL_CMD, + ScriptEvents::PRE_UPDATE_CMD, + ScriptEvents::POST_UPDATE_CMD, + ScriptEvents::PRE_STATUS_CMD, + ScriptEvents::POST_STATUS_CMD, + ScriptEvents::POST_ROOT_PACKAGE_INSTALL, + ScriptEvents::POST_CREATE_PROJECT_CMD + ); + + /** + * @var array Array with script events + */ + protected $scriptEvents = array( + ScriptEvents::PRE_ARCHIVE_CMD, + ScriptEvents::POST_ARCHIVE_CMD, + ScriptEvents::PRE_AUTOLOAD_DUMP, + ScriptEvents::POST_AUTOLOAD_DUMP + ); + protected function configure() { $this @@ -45,20 +69,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $script = $input->getArgument('script'); - if (!in_array($script, array( - ScriptEvents::PRE_INSTALL_CMD, - ScriptEvents::POST_INSTALL_CMD, - ScriptEvents::PRE_UPDATE_CMD, - ScriptEvents::POST_UPDATE_CMD, - ScriptEvents::PRE_STATUS_CMD, - ScriptEvents::POST_STATUS_CMD, - ScriptEvents::POST_ROOT_PACKAGE_INSTALL, - ScriptEvents::POST_CREATE_PROJECT_CMD, - ScriptEvents::PRE_ARCHIVE_CMD, - ScriptEvents::POST_ARCHIVE_CMD, - ScriptEvents::PRE_AUTOLOAD_DUMP, - ScriptEvents::POST_AUTOLOAD_DUMP - ))) { + if (!in_array($script, $this->commandEvents) || !in_array($script, $this->scriptEvents)) { if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script)); } @@ -66,6 +77,10 @@ EOT throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $script)); } - $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); + if (in_array($script, $this->commandEvents)) { + $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); + } else { + $this->getComposer()->getEventDispatcher()->dispatchScript($script); + } } } From e221757197653c3e13640831d984a35e2940d4a1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 23 Mar 2014 23:05:03 +0100 Subject: [PATCH 172/638] Update code to latest EventDispatcher, refs #2722 --- src/Composer/Command/RunScriptCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 0ccf868ad..1f9755347 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -80,7 +80,7 @@ EOT if (in_array($script, $this->commandEvents)) { $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); } else { - $this->getComposer()->getEventDispatcher()->dispatchScript($script); + $this->getComposer()->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev')); } } } From fc9e1d0cff60d4f401425c02fd9fdff7ecce99eb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 24 Mar 2014 00:31:08 +0100 Subject: [PATCH 173/638] CS fixes, refs #2823 --- src/Composer/Autoload/ClassLoader.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index e27edeb1d..a71055531 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -292,19 +292,22 @@ class ClassLoader } $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM if ($file === null && defined('HHVM_VERSION')) { - // Indicates a Hack file (hacklang.org) - $file = $this->findFileWithExtension($class, '.hh'); + $file = $this->findFileWithExtension($class, '.hh'); } if ($file === null) { // Remember that this class does not exist. return $this->classMap[$class] = false; } + return $file; } - private function findFileWithExtension($class, $ext) { + private function findFileWithExtension($class, $ext) + { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; @@ -361,8 +364,6 @@ class ClassLoader if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } - - return null; } } From 5763685e696ea21836b1703f2dc6b862b2bd89b9 Mon Sep 17 00:00:00 2001 From: Jan Brecka Date: Mon, 24 Mar 2014 10:30:50 +0100 Subject: [PATCH 174/638] use statement --- tests/Composer/Test/Autoload/ClassMapGeneratorTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 58e68c0d5..d0dcd2d6b 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -12,6 +12,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\ClassMapGenerator; +use Symfony\Component\Finder\Finder; class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase { @@ -82,7 +83,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $this->markTestSkipped('Finder component is not available'); } - $finder = new \Symfony\Component\Finder\Finder(); + $finder = new Finder(); $finder->files()->in(__DIR__ . '/Fixtures/beta/NamespaceCollision'); $this->assertEqualsNormalized(array( @@ -113,7 +114,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $this->markTestSkipped('Finder component is not available'); } - $finder = new \Symfony\Component\Finder\Finder(); + $finder = new Finder(); $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); ClassMapGenerator::createMap($finder); From de09c5e371fc2d0aec551b8ed5a3adb316694fbf Mon Sep 17 00:00:00 2001 From: Jan Brecka Date: Mon, 24 Mar 2014 10:35:44 +0100 Subject: [PATCH 175/638] remove duplicity --- .../Test/Autoload/ClassMapGeneratorTest.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index d0dcd2d6b..eff1b86a6 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -79,9 +79,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase public function testCreateMapFinderSupport() { - if (!class_exists('Symfony\\Component\\Finder\\Finder')) { - $this->markTestSkipped('Finder component is not available'); - } + $this->checkIfFinderIsAvailable(); $finder = new Finder(); $finder->files()->in(__DIR__ . '/Fixtures/beta/NamespaceCollision'); @@ -110,9 +108,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase */ public function testAmbiguousReference() { - if (!class_exists('Symfony\\Component\\Finder\\Finder')) { - $this->markTestSkipped('Finder component is not available'); - } + $this->checkIfFinderIsAvailable(); $finder = new Finder(); $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); @@ -139,4 +135,11 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase } $this->assertEquals($expected, $actual, $message); } + + private function checkIfFinderIsAvailable() + { + if (!class_exists('Symfony\\Component\\Finder\\Finder')) { + $this->markTestSkipped('Finder component is not available'); + } + } } From b75beda99cec0853535656776d169812761f1703 Mon Sep 17 00:00:00 2001 From: drscre Date: Mon, 24 Mar 2014 13:26:04 +0300 Subject: [PATCH 176/638] Bug in RunScriptCommand::execute() When checking for known command in commandEvents and scriptEvent '&&' should be used instead of '||' --- src/Composer/Command/RunScriptCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 1f9755347..ecdcff23f 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -69,7 +69,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $script = $input->getArgument('script'); - if (!in_array($script, $this->commandEvents) || !in_array($script, $this->scriptEvents)) { + if (!in_array($script, $this->commandEvents) && !in_array($script, $this->scriptEvents)) { if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script)); } From db1e4b7574cb82f60d91219bf8c20e0ec4e902ae Mon Sep 17 00:00:00 2001 From: Tim Roberson Date: Mon, 24 Mar 2014 09:01:35 -0400 Subject: [PATCH 177/638] Add auth for private, non-GitHub repos (#2826) --- src/Composer/Repository/Vcs/GitDriver.php | 29 +++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index ecfad77db..fd1859259 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -71,9 +71,34 @@ class GitDriver extends VcsDriver if (0 !== $this->process->execute('git --version', $ignoredOutput)) { throw new \RuntimeException('Failed to clone '.$this->url.', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); - } + } elseif ( + $this->io->isInteractive() && + preg_match('{(https?://)([^/]+)(.*)$}i', $this->url, $match) && + strpos($output, 'fatal: Authentication failed') !== false + ) { + if ($this->io->hasAuthentication($match[2])) { + $auth = $this->io->getAuthentication($match[2]); + } else { + $this->io->write($this->url.' requires Authentication'); + $auth = array( + 'username' => $this->io->ask('Username: '), + 'password' => $this->io->askAndHideAnswer('Password: '), + ); + } - throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output); + $url = $match[1].urlencode($auth['username']).':'.urlencode($auth['password']).'@'.$match[2].$match[3]; + + $command = sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($this->repoDir)); + + if (0 === $this->process->execute($command, $output)) { + $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); + } else { + $output = $this->process->getErrorOutput(); + throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output); + } + } else { + throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output); + } } } } From b94838169d17476b24497d3f6d23feaf41196e01 Mon Sep 17 00:00:00 2001 From: Jan Brecka Date: Mon, 24 Mar 2014 14:34:02 +0100 Subject: [PATCH 178/638] show warning only instead of throwing exception --- src/Composer/Autoload/AutoloadGenerator.php | 15 ++++++++++++++- src/Composer/Autoload/ClassMapGenerator.php | 12 +++++++++++- src/Composer/Factory.php | 2 +- .../Test/Autoload/ClassMapGeneratorTest.php | 7 +++---- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 3783bd22c..0de53a3bf 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -15,6 +15,7 @@ namespace Composer\Autoload; use Composer\Config; use Composer\EventDispatcher\EventDispatcher; use Composer\Installer\InstallationManager; +use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; @@ -32,11 +33,17 @@ class AutoloadGenerator */ private $eventDispatcher; + /** + * @var IOInterface + */ + private $io; + private $devMode = false; - public function __construct(EventDispatcher $eventDispatcher) + public function __construct(EventDispatcher $eventDispatcher, IOInterface $io=null) { $this->eventDispatcher = $eventDispatcher; + $this->io = $io; } public function setDevMode($devMode = true) @@ -197,6 +204,12 @@ EOF; } } + if ($this->io && count(ClassMapGenerator::$ambiguousReferences) > 0) { + foreach (ClassMapGenerator::$ambiguousReferences as $ambiguousReference) { + $this->io->write('Warning: Ambiguous class "'.$ambiguousReference['class'].'" resolution; defined in "'.$ambiguousReference[0].'" and in "'.$ambiguousReference[1].'" files.'); + } + } + ksort($classMap); foreach ($classMap as $class => $code) { $classmapFile .= ' '.var_export($class, true).' => '.$code; diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 5c2859271..500669a28 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -21,6 +21,12 @@ use Symfony\Component\Finder\Finder; */ class ClassMapGenerator { + + /** + * @var array + */ + public static $ambiguousReferences = array(); + /** * Generate a class map file * @@ -80,7 +86,11 @@ class ClassMapGenerator foreach ($classes as $class) { if (array_key_exists($class, $map)) { - throw new \RuntimeException('Ambiguous class "'.$class.'" resolution; defined in "'.$map[$class].'" and in "'.$filePath.'" files.'); + self::$ambiguousReferences[] = array( + 'class' => $class, + '0' => $map[$class], + '1' => $filePath + ); } $map[$class] = $filePath; diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index d0cd68b79..88fc4132e 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -253,7 +253,7 @@ class Factory $composer->setEventDispatcher($dispatcher); // initialize autoload generator - $generator = new AutoloadGenerator($dispatcher); + $generator = new AutoloadGenerator($dispatcher, $io); $composer->setAutoloadGenerator($generator); // add installers to the manager diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index eff1b86a6..6b5049830 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -102,10 +102,6 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $find->invoke(null, __DIR__.'/no-file'); } - - /** - * @expectedException \RuntimeException - */ public function testAmbiguousReference() { $this->checkIfFinderIsAvailable(); @@ -114,6 +110,9 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); ClassMapGenerator::createMap($finder); + + $this->assertEquals(1, count(ClassMapGenerator::$ambiguousReferences)); + $this->assertEquals('A', ClassMapGenerator::$ambiguousReferences[0]['class']); } /** From 3a3661a0b3ff307e5084fa4846c6e72feb904022 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Mon, 24 Mar 2014 09:43:45 -0500 Subject: [PATCH 179/638] removing dead code lines and excess comments --- src/Composer/Downloader/PerforceDownloader.php | 1 - src/Composer/Repository/Vcs/PerforceDriver.php | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index da70c3469..c2def4ee2 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -22,7 +22,6 @@ use Composer\Util\Perforce; class PerforceDownloader extends VcsDownloader { protected $perforce; -// protected $perforceInjected = false; /** * {@inheritDoc} diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 090de1bb8..9599dd964 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -142,12 +142,6 @@ class PerforceDriver extends VcsDriver $this->composerInfoIdentifier = $identifier; $result = false; return !empty($this->composerInfo); - - if (!empty($this->composerInfo)) { - $result = count($this->composerInfo) > 0; - } - - return $result; } /** From 1f7014888458eb34fd5de3b26809967882dac13a Mon Sep 17 00:00:00 2001 From: Tim Roberson Date: Mon, 24 Mar 2014 11:03:44 -0400 Subject: [PATCH 180/638] Change elseif to if. --- src/Composer/Repository/Vcs/GitDriver.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index fd1859259..06b860227 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -71,7 +71,9 @@ class GitDriver extends VcsDriver if (0 !== $this->process->execute('git --version', $ignoredOutput)) { throw new \RuntimeException('Failed to clone '.$this->url.', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); - } elseif ( + } + + if ( $this->io->isInteractive() && preg_match('{(https?://)([^/]+)(.*)$}i', $this->url, $match) && strpos($output, 'fatal: Authentication failed') !== false From dd1fd0e306dbad073fa8b32c0b23eea52ada8dea Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Mon, 24 Mar 2014 15:19:35 -0500 Subject: [PATCH 181/638] fixed perforce to reference labels instead of invalid tags --- .../Downloader/PerforceDownloader.php | 12 ++++++- src/Composer/Util/Perforce.php | 8 ++--- .../Downloader/PerforceDownloaderTest.php | 33 ++++++++++++++++--- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index c2def4ee2..8892e4b74 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -29,7 +29,7 @@ class PerforceDownloader extends VcsDownloader public function doDownload(PackageInterface $package, $path) { $ref = $package->getSourceReference(); - $label = $package->getPrettyVersion(); + $label = $this->getLabelFromSourceReference($ref); $this->io->write(' Cloning ' . $ref); $this->initPerforce($package, $path); @@ -41,6 +41,16 @@ class PerforceDownloader extends VcsDownloader $this->perforce->cleanupClientSpec(); } + private function getLabelFromSourceReference($ref) + { + $pos = strpos($ref,'@'); + if (false !== $pos) + { + return substr($ref, $pos + 1); + } + return null; + } + public function initPerforce($package, $path) { if (!empty($this->perforce)) { diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index ae6f44d04..7801f966b 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -309,15 +309,15 @@ class Perforce $this->executeCommand($p4CreateClientCommand); } - public function syncCodeBase($label) + public function syncCodeBase($sourceReference) { $prevDir = getcwd(); chdir($this->path); - $p4SyncCommand = $this->generateP4Command('sync -f '); - $p4SyncCommand = $p4SyncCommand . '@' . $label; + if (null != $sourceReference) { + $p4SyncCommand = $p4SyncCommand . '@' . $sourceReference; + } $this->executeCommand($p4SyncCommand); - chdir($prevDir); } diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index c78f1ed13..da93db767 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -112,13 +112,12 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase * @depends testInitPerforceInstantiatesANewPerforceObject * @depends testInitPerforceDoesNothingIfPerforceAlreadySet */ - public function testDoDownload() + public function testDoDownloadWithTag() { //I really don't like this test but the logic of each Perforce method is tested in the Perforce class. Really I am just enforcing workflow. - $ref = 'SOURCE_REF'; - $label = 'LABEL'; + $ref = 'SOURCE_REF@123'; + $label = 123; $this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref)); - $this->package->expects($this->once())->method('getPrettyVersion')->will($this->returnValue($label)); $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); @@ -127,9 +126,33 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $perforce->expects($this->at(2))->method('p4Login')->with($this->identicalTo($this->io)); $perforce->expects($this->at(3))->method('writeP4ClientSpec'); $perforce->expects($this->at(4))->method('connectClient'); - $perforce->expects($this->at(5))->method('syncCodeBase'); + $perforce->expects($this->at(5))->method('syncCodeBase')->with($label); $perforce->expects($this->at(6))->method('cleanupClientSpec'); $this->downloader->setPerforce($perforce); $this->downloader->doDownload($this->package, $this->testPath); } + + /** + * @depends testInitPerforceInstantiatesANewPerforceObject + * @depends testInitPerforceDoesNothingIfPerforceAlreadySet + */ + public function testDoDownloadWithNoTag() + { + $ref = 'SOURCE_REF'; + $label = null; + $this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref)); + $this->io->expects($this->once())->method('write')->with($this->stringContains('Cloning '.$ref)); + $perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec'); + $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock(); + $perforce->expects($this->at(0))->method('initializePath')->with($this->equalTo($this->testPath)); + $perforce->expects($this->at(1))->method('setStream')->with($this->equalTo($ref)); + $perforce->expects($this->at(2))->method('p4Login')->with($this->identicalTo($this->io)); + $perforce->expects($this->at(3))->method('writeP4ClientSpec'); + $perforce->expects($this->at(4))->method('connectClient'); + $perforce->expects($this->at(5))->method('syncCodeBase')->with($label); + $perforce->expects($this->at(6))->method('cleanupClientSpec'); + $this->downloader->setPerforce($perforce); + $this->downloader->doDownload($this->package, $this->testPath); + } + } From b09fb521d281fb832992b1c6bff62580eab499b2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 25 Mar 2014 11:43:41 +0100 Subject: [PATCH 182/638] Fix headers array, fixes #2821 --- src/Composer/Util/GitHub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index b69073d13..a0b2b7460 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -129,7 +129,7 @@ class GitHub // no existing token, create one if (empty($contents['token'])) { - $headers[] = array('Content-Type: application/json'); + $headers[] = 'Content-Type: application/json'; $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( 'retry-auth-failure' => false, From a12c4e2a1722730c199b32e90b3be5240f2f4698 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 25 Mar 2014 08:30:44 -0500 Subject: [PATCH 183/638] Removed getWindowsFlag and setWindowsFlag methods from Perforce object. --- .../Repository/Vcs/PerforceDriver.php | 11 --------- src/Composer/Util/Perforce.php | 10 -------- .../Downloader/PerforceDownloaderTest.php | 4 ++-- .../Repository/Vcs/PerforceDriverTest.php | 20 +++++++++------- tests/Composer/Test/Util/PerforceTest.php | 23 +++++++++++-------- 5 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 9599dd964..e928e5835 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -140,7 +140,6 @@ class PerforceDriver extends VcsDriver { $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; - $result = false; return !empty($this->composerInfo); } @@ -183,14 +182,4 @@ class PerforceDriver extends VcsDriver return $this->branch; } - public function setPerforce(Perforce $perforce) - { - $this->perforce = $perforce; - } - - public function getPerforce() - { - return $this->perforce; - } - } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 7801f966b..e812fbce3 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -370,16 +370,6 @@ class Perforce return; } - public function getWindowsFlag() - { - return $this->windowsFlag; - } - - public function setWindowsFlag($flag) - { - $this->windowsFlag = $flag; - } - public function windowsLogin($password) { $command = $this->generateP4Command(' login -a'); diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index da93db767..7562f7fca 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -32,7 +32,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase protected $repository; protected $testPath; - public function setUp() + protected function setUp() { $this->testPath = sys_get_temp_dir() . '/composer-test'; $this->repoConfig = $this->getRepoConfig(); @@ -44,7 +44,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $this->downloader = new PerforceDownloader($this->io, $this->config, $this->processExecutor); } - public function tearDown() + protected function tearDown() { $this->downloader = null; $this->package = null; diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 9afc57d30..dcb50244a 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -15,6 +15,7 @@ namespace Composer\Test\Repository\Vcs; use Composer\Repository\Vcs\PerforceDriver; use Composer\Util\Filesystem; use Composer\Config; +use Composer\Util\Perforce; /** * @author Matt Whittom @@ -33,7 +34,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase const TEST_DEPOT = 'TEST_DEPOT_CONFIG'; const TEST_BRANCH = 'TEST_BRANCH_CONFIG'; - public function setUp() + protected function setUp() { $this->testPath = sys_get_temp_dir() . '/composer-test'; $this->config = $this->getTestConfig($this->testPath); @@ -43,9 +44,10 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->remoteFileSystem = $this->getMockRemoteFilesystem(); $this->perforce = $this->getMockPerforce(); $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $this->overrideDriverInternalPerforce($this->perforce); } - public function tearDown() + protected function tearDown() { //cleanup directory under test path $fs = new Filesystem; @@ -60,6 +62,14 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->testPath = null; } + protected function overrideDriverInternalPerforce(Perforce $perforce) + { + $reflectionClass = new \ReflectionClass($this->driver); + $property = $reflectionClass->getProperty('perforce'); + $property->setAccessible(true); + $property->setValue($this->driver, $perforce); + } + protected function getTestConfig($testPath) { $config = new Config(); @@ -100,7 +110,6 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase public function testInitializeCapturesVariablesFromRepoConfig() { $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); - $driver->setPerforce($this->perforce); $driver->initialize(); $this->assertEquals(self::TEST_URL, $driver->getUrl()); $this->assertEquals(self::TEST_DEPOT, $driver->getDepot()); @@ -109,7 +118,6 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase public function testInitializeLogsInAndConnectsClient() { - $this->driver->setPerforce($this->perforce); $this->perforce->expects($this->at(0))->method('p4Login')->with($this->identicalTo($this->io)); $this->perforce->expects($this->at(1))->method('checkStream')->with($this->equalTo(self::TEST_DEPOT)); $this->perforce->expects($this->at(2))->method('writeP4ClientSpec'); @@ -125,7 +133,6 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase { $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; - $this->driver->setPerforce($this->perforce); $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array())); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); @@ -140,7 +147,6 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase { $identifier = 'TEST_IDENTIFIER'; $formatted_depot_path = '//' . self::TEST_DEPOT . '/' . $identifier; - $this->driver->setPerforce($this->perforce); $this->perforce->expects($this->any())->method('getComposerInformation')->with($this->equalTo($formatted_depot_path))->will($this->returnValue(array(''))); $this->driver->initialize(); $result = $this->driver->hasComposerFile($identifier); @@ -163,9 +169,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase public function testCleanup() { $this->perforce->expects($this->once())->method('cleanupClientSpec'); - $this->driver->setPerforce($this->perforce); $this->driver->cleanup(); - $this->assertNull($this->driver->getPerforce()); } } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 1b8063258..a5f3e60fa 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -31,15 +31,15 @@ class PerforceTest extends \PHPUnit_Framework_TestCase const TEST_PORT = 'port'; const TEST_PATH = 'path'; - public function setUp() + protected function setUp() { $this->processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $this->repoConfig = $this->getTestRepoConfig(); $this->io = $this->getMockIOInterface(); - $this->perforce = new Perforce($this->repoConfig, self::TEST_PORT, self::TEST_PATH, $this->processExecutor, true, $this->io); + $this->createNewPerforceWithWindowsFlag(true); } - public function tearDown() + protected function tearDown() { $this->perforce = null; $this->io = null; @@ -62,6 +62,11 @@ class PerforceTest extends \PHPUnit_Framework_TestCase return $this->getMock('Composer\IO\IOInterface'); } + protected function createNewPerforceWithWindowsFlag($flag) + { + $this->perforce = new Perforce($this->repoConfig, self::TEST_PORT, self::TEST_PATH, $this->processExecutor, $flag, $this->io); + } + public function testGetClientWithoutStream() { $client = $this->perforce->getClient(); @@ -131,8 +136,8 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserWithUserSetInP4VariablesWithWindowsOS() { + $this->createNewPerforceWithWindowsFlag(true); $this->perforce->setUser(null); - $this->perforce->setWindowsFlag(true); $expectedCommand = 'p4 set'; $callback = function($command, &$output) { @@ -149,8 +154,8 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserWithUserSetInP4VariablesNotWindowsOS() { + $this->createNewPerforceWithWindowsFlag(false); $this->perforce->setUser(null); - $this->perforce->setWindowsFlag(false); $expectedCommand = 'echo $P4USER'; $callback = function($command, &$output) { @@ -179,8 +184,8 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserStoresResponseToQueryForUserWithWindows() { + $this->createNewPerforceWithWindowsFlag(true); $this->perforce->setUser(null); - $this->perforce->setWindowsFlag(true); $expectedQuestion = 'Enter P4 User:'; $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; $this->io->expects($this->at(0)) @@ -196,8 +201,8 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4UserStoresResponseToQueryForUserWithoutWindows() { + $this->createNewPerforceWithWindowsFlag(false); $this->perforce->setUser(null); - $this->perforce->setWindowsFlag(false); $expectedQuestion = 'Enter P4 User:'; $expectedCommand = 'export P4USER=TEST_QUERY_USER'; $this->io->expects($this->at(0)) @@ -226,7 +231,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4PasswordWithPasswordSetInP4VariablesWithWindowsOS() { - $this->perforce->setWindowsFlag(true); + $this->createNewPerforceWithWindowsFlag(true); $expectedCommand = 'p4 set'; $callback = function($command, &$output) { @@ -243,7 +248,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testQueryP4PasswordWithPasswordSetInP4VariablesNotWindowsOS() { - $this->perforce->setWindowsFlag(false); + $this->createNewPerforceWithWindowsFlag(false); $expectedCommand = 'echo $P4PASSWD'; $callback = function($command, &$output) { From 8dc6a13a1c9322b1f98a567d235a269f94a30f76 Mon Sep 17 00:00:00 2001 From: Clark Stuth Date: Tue, 25 Mar 2014 10:48:38 -0500 Subject: [PATCH 184/638] Removing dead code segment. --- src/Composer/Util/Perforce.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index e812fbce3..894a151ef 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -51,13 +51,8 @@ class Perforce public static function create($repoConfig, $port, $path, ProcessExecutor $process, IOInterface $io) { - if (!isset($process)) { - $process = new ProcessExecutor; - } $isWindows = defined('PHP_WINDOWS_VERSION_BUILD'); - $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io); - return $perforce; } From f633ab503ed95b3be0ccc08f533c9eff23a511da Mon Sep 17 00:00:00 2001 From: Sebastian Brandt Date: Fri, 28 Mar 2014 16:27:00 +0100 Subject: [PATCH 185/638] Update SvnDownloader.php --- src/Composer/Downloader/SvnDownloader.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index c31c3df6b..efb9faa25 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -47,8 +47,13 @@ class SvnDownloader extends VcsDownloader throw new \RuntimeException('The .svn directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information'); } + $ignoreAncestryCommand = ""; + if ((int)$this->process->execute("svn --version | egrep 'version [0-9\.]+' -o | tr -d 'version .'") >= 170) { + $ignoreAncestryCommand = " --ignore-ancestry"; + } + $this->io->write(" Checking out " . $ref); - $this->execute($url, "svn switch --ignore-ancestry", sprintf("%s/%s", $url, $ref), $path); + $this->execute($url, "svn switch" . $ignoreAncestry, sprintf("%s/%s", $url, $ref), $path); } /** From 07b8fe81094cc022d54c528af73d8b84bea2194c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 29 Mar 2014 14:16:13 +0100 Subject: [PATCH 186/638] Refactor code so it works cross-platform, refs #2825 --- src/Composer/Downloader/SvnDownloader.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index efb9faa25..8fefbdfc1 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -47,13 +47,15 @@ class SvnDownloader extends VcsDownloader throw new \RuntimeException('The .svn directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information'); } - $ignoreAncestryCommand = ""; - if ((int)$this->process->execute("svn --version | egrep 'version [0-9\.]+' -o | tr -d 'version .'") >= 170) { - $ignoreAncestryCommand = " --ignore-ancestry"; + $flags = ""; + if (0 === $this->process->execute('svn --version', $output)) { + if (preg_match('{(\d+(?:\.\d+)+)}', $output, $match) && version_compare($match[1], '1.7.0', '>=')) { + $flags .= ' --ignore-ancestry'; + } } $this->io->write(" Checking out " . $ref); - $this->execute($url, "svn switch" . $ignoreAncestry, sprintf("%s/%s", $url, $ref), $path); + $this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path); } /** From 3e8a767f8aa24d00e30c31686a7bdfdd2342c9a3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 29 Mar 2014 17:26:43 +0100 Subject: [PATCH 187/638] Attempt using bash on windows if it is available, fixes #2831 --- src/Composer/IO/ConsoleIO.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 39c070db2..607b3a27e 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -15,6 +15,7 @@ namespace Composer\IO; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\HelperSet; +use Composer\Util\ProcessExecutor; /** * The Input/Output helper. @@ -169,8 +170,20 @@ class ConsoleIO extends BaseIO */ public function askAndHideAnswer($question) { + $process = new ProcessExecutor($this); + // handle windows if (defined('PHP_WINDOWS_VERSION_BUILD')) { + // use bash if it's present + if (0 === $process->execute("bash -c 'echo OK'", $output) && 'OK' === rtrim($output)) { + $this->write($question, false); + $value = rtrim(shell_exec('bash -c "stty -echo; read -r mypassword; stty echo; echo $mypassword"')); + $this->write(''); + + return $value; + } + + // fallback to hiddeninput executable $exe = __DIR__.'\\hiddeninput.exe'; // handle code running from a phar From 781c97fa9bb9d1bfe2ecd34c24be8c77809db7ef Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 29 Mar 2014 17:46:55 +0100 Subject: [PATCH 188/638] Refactor to avoid global static state --- src/Composer/Autoload/AutoloadGenerator.php | 12 +++------- src/Composer/Autoload/ClassMapGenerator.php | 24 ++++++++----------- .../Test/Autoload/ClassMapGeneratorTest.php | 14 ++++++++--- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 0de53a3bf..20f8c7570 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -40,7 +40,7 @@ class AutoloadGenerator private $devMode = false; - public function __construct(EventDispatcher $eventDispatcher, IOInterface $io=null) + public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null) { $this->eventDispatcher = $eventDispatcher; $this->io = $io; @@ -184,7 +184,7 @@ EOF; preg_quote($dir), ($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : '' ); - foreach (ClassMapGenerator::createMap($dir, $whitelist) as $class => $path) { + foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io) as $class => $path) { if ('' === $namespace || 0 === strpos($class, $namespace)) { if (!isset($classMap[$class])) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); @@ -198,18 +198,12 @@ EOF; } foreach ($autoloads['classmap'] as $dir) { - foreach (ClassMapGenerator::createMap($dir) as $class => $path) { + foreach (ClassMapGenerator::createMap($dir, null, $this->io) as $class => $path) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); $classMap[$class] = $path.",\n"; } } - if ($this->io && count(ClassMapGenerator::$ambiguousReferences) > 0) { - foreach (ClassMapGenerator::$ambiguousReferences as $ambiguousReference) { - $this->io->write('Warning: Ambiguous class "'.$ambiguousReference['class'].'" resolution; defined in "'.$ambiguousReference[0].'" and in "'.$ambiguousReference[1].'" files.'); - } - } - ksort($classMap); foreach ($classMap as $class => $code) { $classmapFile .= ' '.var_export($class, true).' => '.$code; diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 4805e7308..719a16102 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -12,21 +12,18 @@ */ namespace Composer\Autoload; + use Symfony\Component\Finder\Finder; +use Composer\IO\IOInterface; /** * ClassMapGenerator * * @author Gyula Sallai + * @author Jordi Boggiano */ class ClassMapGenerator { - - /** - * @var array - */ - public static $ambiguousReferences = array(); - /** * Generate a class map file * @@ -54,7 +51,7 @@ class ClassMapGenerator * * @throws \RuntimeException When the path is neither an existing file nor directory */ - public static function createMap($path, $whitelist = null) + public static function createMap($path, $whitelist = null, IOInterface $io = null) { if (is_string($path)) { if (is_file($path)) { @@ -85,15 +82,14 @@ class ClassMapGenerator $classes = self::findClasses($filePath); foreach ($classes as $class) { - if (array_key_exists($class, $map)) { - self::$ambiguousReferences[] = array( - 'class' => $class, - '0' => $map[$class], - '1' => $filePath + if (!isset($map[$class])) { + $map[$class] = $filePath; + } elseif ($io) { + $io->write( + 'Warning: Ambiguous class resolution, "'.$class.'"'. + ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.' ); } - - $map[$class] = $filePath; } } diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 6b5049830..1a855558f 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -109,10 +109,18 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $finder = new Finder(); $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); - ClassMapGenerator::createMap($finder); + $io = $this->getMockBuilder('Composer\IO\ConsoleIO') + ->disableOriginalConstructor() + ->getMock(); - $this->assertEquals(1, count(ClassMapGenerator::$ambiguousReferences)); - $this->assertEquals('A', ClassMapGenerator::$ambiguousReferences[0]['class']); + $a = realpath(__DIR__.'/Fixtures/Ambiguous/A.php'); + $b = realpath(__DIR__.'/Fixtures/Ambiguous/other/A.php'); + + $io->expects($this->once()) + ->method('write') + ->with('Warning: Ambiguous class resolution, "A" was found in both "'.$a.'" and "'.$b.'", the first will be used.'); + + ClassMapGenerator::createMap($finder, null, $io); } /** From 7daf876d704e312868b88b38cd9fce111583d4b6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 29 Mar 2014 18:10:11 +0100 Subject: [PATCH 189/638] Fix test because the order of filesystem iteration is non deterministic --- .../Composer/Test/Autoload/ClassMapGeneratorTest.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 1a855558f..c191c7f1d 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -115,12 +115,22 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $a = realpath(__DIR__.'/Fixtures/Ambiguous/A.php'); $b = realpath(__DIR__.'/Fixtures/Ambiguous/other/A.php'); + $msg = ''; $io->expects($this->once()) ->method('write') - ->with('Warning: Ambiguous class resolution, "A" was found in both "'.$a.'" and "'.$b.'", the first will be used.'); + ->will($this->returnCallback(function ($text) use (&$msg) { + $msg = $text; + })); + + $messages = array( + 'Warning: Ambiguous class resolution, "A" was found in both "'.$a.'" and "'.$b.'", the first will be used.', + 'Warning: Ambiguous class resolution, "A" was found in both "'.$b.'" and "'.$a.'", the first will be used.', + ); ClassMapGenerator::createMap($finder, null, $io); + + $this->assertTrue(in_array($msg, $messages, true), $msg.' not found in expected messages ('.var_export($messages, true).')'); } /** From e3be04c4430b996cec6a8e4d16e4ad481306fbac Mon Sep 17 00:00:00 2001 From: Daniel Karp Date: Sun, 30 Mar 2014 12:53:15 -0400 Subject: [PATCH 190/638] Don't show Ambiguous class resolution warning if class when not ambiguous if a class or interface is defined twice in the same class, this should not produce a warning --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- .../Test/Autoload/ClassMapGeneratorTest.php | 21 +++++++++++++++++++ .../Test/Autoload/Fixtures/Unambiguous/A.php | 6 ++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 719a16102..257930f59 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -84,7 +84,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (!isset($map[$class])) { $map[$class] = $filePath; - } elseif ($io) { + } elseif ($io && $map[$class] !== $filePath) { $io->write( 'Warning: Ambiguous class resolution, "'.$class.'"'. ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.' diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index c191c7f1d..fa9380e5f 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -133,6 +133,27 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $this->assertTrue(in_array($msg, $messages, true), $msg.' not found in expected messages ('.var_export($messages, true).')'); } + /** + * If one file has a class or interface defined more than once, + * an ambiguous reference warning should not be produced + */ + public function testUnambiguousReference() + { + $this->checkIfFinderIsAvailable(); + + $finder = new Finder(); + $finder->files()->in(__DIR__ . '/Fixtures/Unambiguous'); + + $io = $this->getMockBuilder('Composer\IO\ConsoleIO') + ->disableOriginalConstructor() + ->getMock(); + + $io->expects($this->never()) + ->method('write'); + + ClassMapGenerator::createMap($finder, null, $io); + } + /** * @expectedException \RuntimeException * @expectedExceptionMessage Could not scan for classes inside diff --git a/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php b/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php new file mode 100644 index 000000000..d88eba6e8 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php @@ -0,0 +1,6 @@ + Date: Sun, 30 Mar 2014 23:33:30 +0300 Subject: [PATCH 191/638] Specify how ~ constraints handle pre-releases The "next significant release" a.k.a as the tilde version constraint would not install a pre-release (alpha, beta, RC) which is not in the same version namespace. But in the examples so far it was shown as `~1.2` equals `>=1.2,<2.0` which would actually install `2.0-beta.1`, because it the pre-release is before the `2.0` release. See https://github.com/composer/getcomposer.org/issues/64 --- doc/01-basic-usage.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 1aa3eee05..dc05489c6 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -82,6 +82,10 @@ including, 2.0). Since in theory there should be no backwards compatibility breaks until 2.0, that works well. Another way of looking at it is that using `~` specifies a minimum version, but allows the last digit specified to go up. +> **Note:** Though `2.0-beta.1` is considered before `2.0`, a version constraint +> like `~1.2` would not install it. So it will install every release in the same +> *namespace* and not in the `2.0` namespace. + ### Stability By default only stable releases are taken into consideration. If you would like From 5078d7c4fb466a2089e0c5513b456b1e3f5c0734 Mon Sep 17 00:00:00 2001 From: Alex Romanenko Date: Mon, 31 Mar 2014 09:39:58 +0400 Subject: [PATCH 192/638] Fix space in password for git auth --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 06b860227..a37633655 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -88,7 +88,7 @@ class GitDriver extends VcsDriver ); } - $url = $match[1].urlencode($auth['username']).':'.urlencode($auth['password']).'@'.$match[2].$match[3]; + $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; $command = sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($this->repoDir)); From 6e16d53defef5416e268b48988b52ed008d5499e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 31 Mar 2014 14:59:00 +0200 Subject: [PATCH 193/638] Also use rawurlencode in git downloader, refs #2859 --- src/Composer/Downloader/GitDownloader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 073554522..3186117ae 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -353,7 +353,7 @@ class GitDownloader extends VcsDownloader if ($this->io->hasAuthentication($match[1])) { $auth = $this->io->getAuthentication($match[1]); - $url = 'https://'.urlencode($auth['username']) . ':' . urlencode($auth['password']) . '@'.$match[1].'/'.$match[2].'.git'; + $url = 'https://'.rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@'.$match[1].'/'.$match[2].'.git'; $command = call_user_func($commandCallable, $url); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { @@ -376,7 +376,7 @@ class GitDownloader extends VcsDownloader ); } - $url = $match[1].urlencode($auth['username']).':'.urlencode($auth['password']).'@'.$match[2].$match[3]; + $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; $command = call_user_func($commandCallable, $url); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { From f56d3150364b4737a0dfe50a8dc9edfb809a497c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 31 Mar 2014 15:14:47 +0200 Subject: [PATCH 194/638] Ensure both bash and stty are present to assume cygwin, fixes #2857 --- src/Composer/IO/ConsoleIO.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 607b3a27e..c7703edfc 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -15,7 +15,7 @@ namespace Composer\IO; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\HelperSet; -use Composer\Util\ProcessExecutor; +use Symfony\Component\Process\ExecutableFinder; /** * The Input/Output helper. @@ -170,12 +170,12 @@ class ConsoleIO extends BaseIO */ public function askAndHideAnswer($question) { - $process = new ProcessExecutor($this); - // handle windows if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $finder = new ExecutableFinder(); + // use bash if it's present - if (0 === $process->execute("bash -c 'echo OK'", $output) && 'OK' === rtrim($output)) { + if ($finder->find('bash') && $finder->find('stty')) { $this->write($question, false); $value = rtrim(shell_exec('bash -c "stty -echo; read -r mypassword; stty echo; echo $mypassword"')); $this->write(''); From 355dc67038287f5d9ce4a188c5be5fdd316b36f8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 31 Mar 2014 16:33:11 +0200 Subject: [PATCH 195/638] Tweak wording a bit --- doc/01-basic-usage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index dc05489c6..8ac5066fc 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -82,9 +82,9 @@ including, 2.0). Since in theory there should be no backwards compatibility breaks until 2.0, that works well. Another way of looking at it is that using `~` specifies a minimum version, but allows the last digit specified to go up. -> **Note:** Though `2.0-beta.1` is considered before `2.0`, a version constraint -> like `~1.2` would not install it. So it will install every release in the same -> *namespace* and not in the `2.0` namespace. +> **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint +> like `~1.2` would not install it. As said above `~1.2` only means the `.2` +> can change but the `1.` part is fixed. ### Stability From 8974a1ac92a3a7ba38aefc60a7393c306ba415c0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 31 Mar 2014 21:36:48 +0200 Subject: [PATCH 196/638] Ignore ambiguous classes in tests/fixtures dirs, fixes #2858 --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- tests/Composer/Test/Autoload/ClassMapGeneratorTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 257930f59..4e7dcf496 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -84,7 +84,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (!isset($map[$class])) { $map[$class] = $filePath; - } elseif ($io && $map[$class] !== $filePath) { + } elseif ($io && $map[$class] !== $filePath && preg_match('{/(test|fixture)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { $io->write( 'Warning: Ambiguous class resolution, "'.$class.'"'. ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.' diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index fa9380e5f..07e95d525 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -102,6 +102,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $find->invoke(null, __DIR__.'/no-file'); } + public function testAmbiguousReference() { $this->checkIfFinderIsAvailable(); From 090230519698ddb34eb710a16fa2c49fc719cea7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 1 Apr 2014 14:38:31 +0200 Subject: [PATCH 197/638] Fix classmap generator warnings and tests --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- .../Test/Autoload/ClassMapGeneratorTest.php | 18 +++++++++++++++--- .../Test/Autoload/Fixtures/Ambiguous/A.php | 6 ------ .../Autoload/Fixtures/Ambiguous/other/A.php | 6 ------ .../Fixtures/Unambiguous/tests/FixtureA.php | 3 +++ .../Fixtures/Unambiguous/tests/FixtureA2.php | 3 +++ 6 files changed, 22 insertions(+), 16 deletions(-) delete mode 100644 tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php delete mode 100644 tests/Composer/Test/Autoload/Fixtures/Ambiguous/other/A.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/tests/FixtureA.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/tests/FixtureA2.php diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 4e7dcf496..2cdb920a8 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -84,7 +84,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (!isset($map[$class])) { $map[$class] = $filePath; - } elseif ($io && $map[$class] !== $filePath && preg_match('{/(test|fixture)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { + } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { $io->write( 'Warning: Ambiguous class resolution, "'.$class.'"'. ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.' diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 07e95d525..570eeab10 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -13,6 +13,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\ClassMapGenerator; use Symfony\Component\Finder\Finder; +use Composer\Util\Filesystem; class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase { @@ -107,15 +108,23 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase { $this->checkIfFinderIsAvailable(); + $tempDir = sys_get_temp_dir().'/ComposerTestAmbiguousRefs'; + if (!is_dir($tempDir.'/other')) { + mkdir($tempDir.'/other', 0777, true); + } + $finder = new Finder(); - $finder->files()->in(__DIR__ . '/Fixtures/Ambiguous'); + $finder->files()->in($tempDir); $io = $this->getMockBuilder('Composer\IO\ConsoleIO') ->disableOriginalConstructor() ->getMock(); - $a = realpath(__DIR__.'/Fixtures/Ambiguous/A.php'); - $b = realpath(__DIR__.'/Fixtures/Ambiguous/other/A.php'); + file_put_contents($tempDir.'/A.php', "expects($this->once()) @@ -132,6 +141,9 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase ClassMapGenerator::createMap($finder, null, $io); $this->assertTrue(in_array($msg, $messages, true), $msg.' not found in expected messages ('.var_export($messages, true).')'); + + $fs = new Filesystem(); + $fs->removeDirectory($tempDir); } /** diff --git a/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php b/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php deleted file mode 100644 index ee9325efa..000000000 --- a/tests/Composer/Test/Autoload/Fixtures/Ambiguous/A.php +++ /dev/null @@ -1,6 +0,0 @@ - Date: Tue, 1 Apr 2014 09:20:11 -0700 Subject: [PATCH 198/638] update homebrew instructions --- doc/00-intro.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 4eeabddf2..dbe19ac56 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -95,13 +95,12 @@ Then, just run `composer` in order to run Composer instead of `php composer.phar Composer is part of the homebrew-php project. -1. Tap the homebrew-php repository into your brew installation if you haven't done - so yet: `brew tap josegonzalez/homebrew-php` -2. Run `brew install josegonzalez/php/composer`. -3. Use Composer with the `composer` command. - -> **Note:** If you receive an error saying PHP53 or higher is missing use this command to install php -> `brew install php53-intl` +```sh +brew update +brew tap josegonzalez/homebrew-php +brew install php53-intl # if you don't already have new-ish php +brew install josegonzalez/php/composer +``` ## Installation - Windows From 8c382d49299a9e3e0d4d4e848d9309571c12f95f Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 1 Apr 2014 09:46:16 -0700 Subject: [PATCH 199/638] forget a homebrew step that's required for php installation --- doc/00-intro.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index dbe19ac56..ea62f2299 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -98,7 +98,8 @@ Composer is part of the homebrew-php project. ```sh brew update brew tap josegonzalez/homebrew-php -brew install php53-intl # if you don't already have new-ish php +brew tap homebrew/versions +brew install php53-intl brew install josegonzalez/php/composer ``` From c16f83a8c84c69daec72fc8fca91b354fcc7f09f Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 1 Apr 2014 13:07:50 -0700 Subject: [PATCH 200/638] use php 5.5 in homebrew instructions --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index ea62f2299..451e9c359 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -99,7 +99,7 @@ Composer is part of the homebrew-php project. brew update brew tap josegonzalez/homebrew-php brew tap homebrew/versions -brew install php53-intl +brew install php55-intl brew install josegonzalez/php/composer ``` From 244e865a359108f3126edd5e157fbc7ce07b72a9 Mon Sep 17 00:00:00 2001 From: Pierre du Plessis Date: Wed, 2 Apr 2014 08:54:24 +0200 Subject: [PATCH 201/638] Fix typo --- src/Composer/Command/CreateProjectCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 87369dcaf..643e5a24e 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -56,7 +56,7 @@ class CreateProjectCommand extends Command ->setDefinition(array( new InputArgument('package', InputArgument::OPTIONAL, 'Package name to be installed'), new InputArgument('directory', InputArgument::OPTIONAL, 'Directory where the files should be created'), - new InputArgument('version', InputArgument::OPTIONAL, 'Version, will defaults to latest'), + new InputArgument('version', InputArgument::OPTIONAL, 'Version, will default to latest'), new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), From a8ec134c2c34482bedf08c6d022cbbc5ca3fa7bc Mon Sep 17 00:00:00 2001 From: David Zuelke Date: Sun, 6 Apr 2014 00:07:20 +0200 Subject: [PATCH 202/638] Only print suggestions in dev mode There is no need to clutter the screen/logs/whatever with suggestions in production installs. If someone didn't care about them in dev mode, they certainly won't in production :) --- src/Composer/Installer.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 839fc45e4..cfc08c23d 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -223,16 +223,18 @@ class Installer } $this->installationManager->notifyInstalls(); - // output suggestions - foreach ($this->suggestedPackages as $suggestion) { - $target = $suggestion['target']; - foreach ($installedRepo->getPackages() as $package) { - if (in_array($target, $package->getNames())) { - continue 2; + // output suggestions if we're in dev mode + if (!$this->devMode) { + foreach ($this->suggestedPackages as $suggestion) { + $target = $suggestion['target']; + foreach ($installedRepo->getPackages() as $package) { + if (in_array($target, $package->getNames())) { + continue 2; + } } - } - $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); + $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); + } } if (!$this->dryRun) { From a931faaa3446df42537e4d740de194a9df1ab41a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 6 Apr 2014 22:44:56 +0200 Subject: [PATCH 203/638] Fix boolean check fail from a8ec134c2c34482bedf08c6d022cbbc5ca3fa7bc --- src/Composer/Installer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index cfc08c23d..ffbbbb9dd 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -224,7 +224,7 @@ class Installer $this->installationManager->notifyInstalls(); // output suggestions if we're in dev mode - if (!$this->devMode) { + if ($this->devMode) { foreach ($this->suggestedPackages as $suggestion) { $target = $suggestion['target']; foreach ($installedRepo->getPackages() as $package) { From 3c0edd8c7fccacdbb04a6c9964944c4b843b3b81 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 7 Apr 2014 11:10:26 +0200 Subject: [PATCH 204/638] Process remove ops first, fixes #2874 --- src/Composer/Installer.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index ffbbbb9dd..03e6beb92 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -481,6 +481,7 @@ class Installer } $operations = $this->movePluginsToFront($operations); + $operations = $this->moveUninstallsToFront($operations); foreach ($operations as $operation) { // collect suggestions @@ -591,6 +592,26 @@ class Installer return array_merge($installerOps, $operations); } + /** + * Removals of packages should be executed before installations in + * case two packages resolve to the same path (due to custom installers) + * + * @param OperationInterface[] $operations + * @return OperationInterface[] reordered operation list + */ + private function moveUninstallsToFront(array $operations) + { + $uninstOps = array(); + foreach ($operations as $idx => $op) { + if ($op instanceof UninstallOperation) { + $uninstOps[] = $op; + unset($operations[$idx]); + } + } + + return array_merge($uninstOps, $operations); + } + private function createPool($withDevReqs) { $minimumStability = $this->package->getMinimumStability(); From d99212da522db271e414d7fa917f871789aa0a48 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 7 Apr 2014 11:10:57 +0200 Subject: [PATCH 205/638] Fix integration tests --- .../Test/Fixtures/installer/install-dev.test | 4 +-- .../Fixtures/installer/suggest-installed.test | 4 +-- .../Test/Fixtures/installer/suggest-prod.test | 26 +++++++++++++++++++ .../Fixtures/installer/suggest-replaced.test | 4 +-- .../installer/suggest-uninstalled.test | 4 +-- .../installer/update-all-dry-run.test | 2 +- .../Test/Fixtures/installer/update-all.test | 4 +-- .../installer/update-whitelist-patterns.test | 2 +- tests/Composer/Test/InstallerTest.php | 4 +-- 9 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/suggest-prod.test diff --git a/tests/Composer/Test/Fixtures/installer/install-dev.test b/tests/Composer/Test/Fixtures/installer/install-dev.test index 3b03675bb..b6543fb1b 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev.test @@ -19,7 +19,7 @@ Installs a package in dev env } } --RUN-- -install --dev +install --EXPECT-- Installing a/a (1.0.0) -Installing a/b (1.0.0) \ No newline at end of file +Installing a/b (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-installed.test b/tests/Composer/Test/Fixtures/installer/suggest-installed.test index f46102d0a..94f6c2016 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-installed.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-installed.test @@ -20,10 +20,10 @@ Suggestions are not displayed for installed packages install --EXPECT-OUTPUT-- Loading composer repositories with package information -Installing dependencies +Installing dependencies (including require-dev) Writing lock file Generating autoload files --EXPECT-- Installing a/a (1.0.0) -Installing b/b (1.0.0) \ No newline at end of file +Installing b/b (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-prod.test b/tests/Composer/Test/Fixtures/installer/suggest-prod.test new file mode 100644 index 000000000..290ccf4bb --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/suggest-prod.test @@ -0,0 +1,26 @@ +--TEST-- +Suggestions are not displayed in non-dev mode +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } } + ] + } + ], + "require": { + "a/a": "1.0.0" + } +} +--RUN-- +install --no-dev +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Installing dependencies +Writing lock file +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test index d1e8f6102..0817c7e08 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test @@ -20,10 +20,10 @@ Suggestions are not displayed for packages if they are replaced install --EXPECT-OUTPUT-- Loading composer repositories with package information -Installing dependencies +Installing dependencies (including require-dev) Writing lock file Generating autoload files --EXPECT-- Installing a/a (1.0.0) -Installing c/c (1.0.0) \ No newline at end of file +Installing c/c (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test index d2ea37766..d7e026e98 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test @@ -18,10 +18,10 @@ Suggestions are displayed install --EXPECT-OUTPUT-- Loading composer repositories with package information -Installing dependencies +Installing dependencies (including require-dev) a/a suggests installing b/b (an obscure reason) Writing lock file Generating autoload files --EXPECT-- -Installing a/a (1.0.0) \ No newline at end of file +Installing a/a (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test b/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test index 191f97495..cca859e9f 100644 --- a/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test +++ b/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test @@ -34,7 +34,7 @@ Updates updateable packages in dry-run mode { "name": "a/b", "version": "1.0.0" } ] --RUN-- -update --dev --dry-run +update --dry-run --EXPECT-- Updating a/a (1.0.0) to a/a (1.0.1) Updating a/b (1.0.0) to a/b (2.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-all.test b/tests/Composer/Test/Fixtures/installer/update-all.test index ad5e1d3be..a9bb435a1 100644 --- a/tests/Composer/Test/Fixtures/installer/update-all.test +++ b/tests/Composer/Test/Fixtures/installer/update-all.test @@ -34,7 +34,7 @@ Updates updateable packages { "name": "a/b", "version": "1.0.0" } ] --RUN-- -update --dev +update --EXPECT-- Updating a/a (1.0.0) to a/a (1.0.1) -Updating a/b (1.0.0) to a/b (2.0.0) \ No newline at end of file +Updating a/b (1.0.0) to a/b (2.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test index e8aa593c0..de1fb1b73 100644 --- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test @@ -45,4 +45,4 @@ update vendor/Test* exact/Test-Package notexact/Test all/* no/reg?xp Updating vendor/Test-Package (1.0) to vendor/Test-Package (2.0) Updating exact/Test-Package (1.0) to exact/Test-Package (2.0) Updating all/Package1 (1.0) to all/Package1 (2.0) -Updating all/Package2 (1.0) to all/Package2 (2.0) \ No newline at end of file +Updating all/Package2 (1.0) to all/Package2 (2.0) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index cc17973bc..a88ce7c21 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -203,7 +203,7 @@ class InstallerTest extends TestCase $application = new Application; $application->get('install')->setCode(function ($input, $output) use ($installer) { $installer - ->setDevMode($input->getOption('dev')) + ->setDevMode(!$input->getOption('no-dev')) ->setDryRun($input->getOption('dry-run')); return $installer->run(); @@ -211,7 +211,7 @@ class InstallerTest extends TestCase $application->get('update')->setCode(function ($input, $output) use ($installer) { $installer - ->setDevMode($input->getOption('dev')) + ->setDevMode(!$input->getOption('no-dev')) ->setUpdate(true) ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) From 5b7e39e9198bf66f80933c669db716dcb5600c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=BChn?= Date: Mon, 7 Apr 2014 14:57:45 +0200 Subject: [PATCH 206/638] fixed: dead symlink breaks gc --- src/Composer/Cache.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 6fe01702b..f8d64cae1 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -151,14 +151,14 @@ class Cache $finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s')); foreach ($finder as $file) { - unlink($file->getRealPath()); + unlink($file->getPathname()); } $totalSize = $this->filesystem->size($this->root); if ($totalSize > $maxSize) { $iterator = $this->getFinder()->sortByAccessedTime()->getIterator(); while ($totalSize > $maxSize && $iterator->valid()) { - $filepath = $iterator->current()->getRealPath(); + $filepath = $iterator->current()->getPathname(); $totalSize -= $this->filesystem->size($filepath); unlink($filepath); $iterator->next(); From c0086ba1e33faba2f61e44a04f18ac65129a8a6b Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Wed, 9 Apr 2014 13:40:55 +0200 Subject: [PATCH 207/638] Clarify log messages when skipping existing bins --- src/Composer/Installer/LibraryInstaller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index b1677cec2..646f801d3 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -196,7 +196,7 @@ class LibraryInstaller implements InstallerInterface foreach ($binaries as $bin) { $binPath = $this->getInstallPath($package).'/'.$bin; if (!file_exists($binPath)) { - $this->io->write(' Skipped installation of '.$bin.' for package '.$package->getName().': file not found in package'); + $this->io->write(' Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package'); continue; } @@ -215,7 +215,7 @@ class LibraryInstaller implements InstallerInterface // is a fresh install of the vendor. @chmod($link, 0777 & ~umask()); } - $this->io->write(' Skipped installation of '.$bin.' for package '.$package->getName().': name conflicts with an existing file'); + $this->io->write(' Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file'); continue; } if (defined('PHP_WINDOWS_VERSION_BUILD')) { @@ -225,7 +225,7 @@ class LibraryInstaller implements InstallerInterface @chmod($link, 0777 & ~umask()); $link .= '.bat'; if (file_exists($link)) { - $this->io->write(' Skipped installation of '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed'); + $this->io->write(' Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed'); } } if (!file_exists($link)) { From a09959173513da3c15ce070ef53c72ab9b62dfb1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 9 Apr 2014 14:46:32 +0200 Subject: [PATCH 208/638] Adjustments to unbound constraint docs --- ...-unbound-version-constraints-a-bad-idea.md | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md index a85bf35f6..ac9cfa79b 100644 --- a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md +++ b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md @@ -1,29 +1,21 @@ # Why are unbound version constraints a bad idea? -A version constraint without an upper bound will allow any future version of -the dependency, even newer major version breaking backward compatibility -(which is the only reason to bump the major version when following semver). +A version constraint without an upper bound such as `*` or `>=3.4` will allow +updates to any future version of the dependency. This includes major versions +breaking backward compatibility. Once a release of your package is tagged, you cannot tweak its dependencies -anymore in case a dependency breaks BC (you have to do a new release but the -previous one stays broken). +anymore in case a dependency breaks BC - you have to do a new release but the +previous one stays broken. -These leaves you with 3 alternatives to avoid having broken releases: +The only good alternative is to define an upper bound on your constraints, +which you can increase in a new release after testing that your package is +compatible with the new major version of your dependency. -- defining an upper bound on your constraint (which you will increase in a - new release after testing that your package is compatible with the new - version) +For example instead of using `>=3.4` you should use `~3.4` which allows all +versions up to `3.999` but does not include `4.0` and above. The `~` operator +works very well with libraries follow [semantic versioning](http://semver.org). -- knowing all future changes of your dependency to guarantee the compatibility - of the current code. Forget this alternative unless you are Chuck Norris :) - -- never release your package, but this means that all users will have to - whitelist the dev versions to install it (and complain about it) - -The recommended way is of course to define an upper bound on your constraint, -so Composer will show you a warning for unbound constraints when validating -your `composer.json` file. - -As a package maintainer, you can make the life of your users easier by -providing an [alias version](../articles/aliases.md) for your development +**Note:** As a package maintainer, you can make the life of your users easier +by providing an [alias version](../articles/aliases.md) for your development branch to allow it to match bound constraints. From f6bf7e4a7b6b631adb8f7c556da418a3b380d68f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 9 Apr 2014 14:50:55 +0200 Subject: [PATCH 209/638] Add dev-master example --- doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md index ac9cfa79b..183403948 100644 --- a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md +++ b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md @@ -1,8 +1,8 @@ # Why are unbound version constraints a bad idea? -A version constraint without an upper bound such as `*` or `>=3.4` will allow -updates to any future version of the dependency. This includes major versions -breaking backward compatibility. +A version constraint without an upper bound such as `*`, `>=3.4` or +`dev-master` will allow updates to any future version of the dependency. +This includes major versions breaking backward compatibility. Once a release of your package is tagged, you cannot tweak its dependencies anymore in case a dependency breaks BC - you have to do a new release but the From e7b82cdd886f305cd5b86fbf52d80e2d9020c9fb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 9 Apr 2014 15:23:43 +0200 Subject: [PATCH 210/638] Only check in require and for non-platform packages, add flag to make this warning optional, refs #2320 --- .../Package/Loader/ValidatingArrayLoader.php | 27 +++++++++------- .../Repository/PlatformRepository.php | 2 +- src/Composer/Util/ConfigValidator.php | 2 +- .../Loader/ValidatingArrayLoaderTest.php | 31 ++++++++++--------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 383956e79..ddd9ca328 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -16,24 +16,30 @@ use Composer\Package; use Composer\Package\BasePackage; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Version\VersionParser; +use Composer\Repository\PlatformRepository; /** * @author Jordi Boggiano */ class ValidatingArrayLoader implements LoaderInterface { + const CHECK_ALL = 1; + const CHECK_UNBOUND_CONSTRAINTS = 1; + private $loader; private $versionParser; private $errors; private $warnings; private $config; private $strictName; + private $flags; - public function __construct(LoaderInterface $loader, $strictName = true, VersionParser $parser = null) + public function __construct(LoaderInterface $loader, $strictName = true, VersionParser $parser = null, $flags = 0) { $this->loader = $loader; $this->versionParser = $parser ?: new VersionParser(); $this->strictName = $strictName; + $this->flags = $flags; } public function load(array $config, $class = 'Composer\Package\CompletePackage') @@ -163,20 +169,17 @@ class ValidatingArrayLoader implements LoaderInterface continue; } - if ('conflict' === $linkType || 'require-dev' === $linkType) { - continue; // conflict can be unbound, and require-dev constraints will not impact shared libraries as they are root-only - } - - if ($linkConstraint->matches($unboundConstraint)) { - $this->warnings[] = $linkType.'.'.$package.' : unbound version constraint detected ('.$constraint.')'; - unset($this->config[$linkType][$package]); + // check requires for unbound constraints on non-platform packages + if ( + ($this->flags & self::CHECK_UNBOUND_CONSTRAINTS) + && 'require' === $linkType + && $linkConstraint->matches($unboundConstraint) + && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $package) + ) { + $this->warnings[] = $linkType.'.'.$package.' : unbound version constraints ('.$constraint.') should be avoided'; } } } - - if (empty($this->config[$linkType])) { - unset($this->config[$linkType]); - } } } diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 2b462fca9..f1a27c648 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -21,7 +21,7 @@ use Composer\Plugin\PluginInterface; */ class PlatformRepository extends ArrayRepository { - const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|(?:ext|lib)-[^/]+)$}i'; + const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|hhvm|(?:ext|lib)-[^/]+)$}i'; protected function initialize() { diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 5bd915388..731575e90 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -119,7 +119,7 @@ class ConfigValidator } try { - $loader = new ValidatingArrayLoader(new ArrayLoader()); + $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, ValidatingArrayLoader::CHECK_ALL); if (!isset($manifest['version'])) { $manifest['version'] = '1.0.0'; } diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index b64fc0624..09bb4afd9 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -29,7 +29,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase ->method('load') ->with($config); - $loader = new ValidatingArrayLoader($internalLoader); + $loader = new ValidatingArrayLoader($internalLoader, true, null, ValidatingArrayLoader::CHECK_ALL); $loader->load($config); } @@ -163,7 +163,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase public function testLoadFailureThrowsException($config, $expectedErrors) { $internalLoader = $this->getMock('Composer\Package\Loader\LoaderInterface'); - $loader = new ValidatingArrayLoader($internalLoader); + $loader = new ValidatingArrayLoader($internalLoader, true, null, ValidatingArrayLoader::CHECK_ALL); try { $loader->load($config); $this->fail('Expected exception to be thrown'); @@ -181,7 +181,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase public function testLoadWarnings($config, $expectedWarnings) { $internalLoader = $this->getMock('Composer\Package\Loader\LoaderInterface'); - $loader = new ValidatingArrayLoader($internalLoader); + $loader = new ValidatingArrayLoader($internalLoader, true, null, ValidatingArrayLoader::CHECK_ALL); $loader->load($config); $warnings = $loader->getWarnings(); @@ -193,15 +193,19 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase /** * @dataProvider warningProvider */ - public function testLoadSkipsWarningDataWhenIgnoringErrors($config) + public function testLoadSkipsWarningDataWhenIgnoringErrors($config, $expectedWarnings, $mustCheck = true) { + if (!$mustCheck) { + $this->assertTrue(true); + return; + } $internalLoader = $this->getMock('Composer\Package\Loader\LoaderInterface'); $internalLoader ->expects($this->once()) ->method('load') ->with(array('name' => 'a/b')); - $loader = new ValidatingArrayLoader($internalLoader); + $loader = new ValidatingArrayLoader($internalLoader, true, null, ValidatingArrayLoader::CHECK_ALL); $config['name'] = 'a/b'; $loader->load($config); } @@ -297,20 +301,17 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'require' => array( 'foo/baz' => '*', 'bar/baz' => '>=1.0', - ), - 'provide' => array( 'bar/foo' => 'dev-master', - ), - 'replace' => array( 'bar/hacked' => '@stable', - ) + ), ), array( - 'require.foo/baz : unbound version constraint detected (*)', - 'require.bar/baz : unbound version constraint detected (>=1.0)', - 'provide.bar/foo : unbound version constraint detected (dev-master)', - 'replace.bar/hacked : unbound version constraint detected (@stable)', - ) + 'require.foo/baz : unbound version constraints (*) should be avoided', + 'require.bar/baz : unbound version constraints (>=1.0) should be avoided', + 'require.bar/foo : unbound version constraints (dev-master) should be avoided', + 'require.bar/hacked : unbound version constraints (@stable) should be avoided', + ), + false ), ); } From 78f2ad95a9531884d5d6cea58b7f2dc7942113b7 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Mar 2014 17:27:38 +0100 Subject: [PATCH 211/638] Correctly add decision reason to transaction --- src/Composer/DependencyResolver/Transaction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index 4c1fb124a..0c30f06df 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -79,6 +79,7 @@ class Transaction foreach ($this->decisions as $i => $decision) { $literal = $decision[Decisions::DECISION_LITERAL]; + $reason = $decision[Decisions::DECISION_REASON]; $package = $this->pool->literalToPackage($literal); if ($literal <= 0 && From ddec582ca12991a82247031d119a0a1790e00e28 Mon Sep 17 00:00:00 2001 From: Paul Tarjan Date: Wed, 9 Apr 2014 15:40:12 -0700 Subject: [PATCH 212/638] use HHVM_VERSION this one is better (they are the same) --- src/Composer/Repository/PlatformRepository.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index f1a27c648..0c6a5d9ce 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -146,12 +146,12 @@ class PlatformRepository extends ArrayRepository parent::addPackage($lib); } - if (defined('HPHP_VERSION')) { + if (defined('HHVM_VERSION')) { try { - $prettyVersion = HPHP_VERSION; + $prettyVersion = HHVM_VERSION; $version = $versionParser->normalize($prettyVersion); } catch (\UnexpectedValueException $e) { - $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', HPHP_VERSION); + $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', HHVM_VERSION); $version = $versionParser->normalize($prettyVersion); } From 465f0e376135a96814ac4c75d4d943f620615c91 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 9 Apr 2014 19:46:24 -0700 Subject: [PATCH 213/638] When requiring a package remove it from require-dev and vice versa --- src/Composer/Command/RequireCommand.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 4a005d809..bd969cebd 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -81,6 +81,7 @@ EOT $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; + $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); @@ -90,9 +91,13 @@ EOT $versionParser->parseConstraints($constraint); } - if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey)) { + if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; + + if (isset($composer[$removeKey][$package])) { + unset($composer[$removeKey][$package]); + } } $composer[$requireKey] = $baseRequirements; @@ -134,7 +139,7 @@ EOT return $status; } - private function updateFileCleanly($json, array $base, array $new, $requireKey) + private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey) { $contents = file_get_contents($json->getPath()); @@ -144,6 +149,9 @@ EOT if (!$manipulator->addLink($requireKey, $package, $constraint)) { return false; } + if (!$manipulator->removeSubNode($removeKey, $package)) { + return false; + } } file_put_contents($json->getPath(), $manipulator->getContents()); From a2b227740adcbe5e45026e88a0e1a512caae811c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 9 Apr 2014 20:08:10 -0700 Subject: [PATCH 214/638] Add an --update-no-dev option to the require command --- src/Composer/Command/RequireCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 4a005d809..fa327a00e 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -42,6 +42,7 @@ class RequireCommand extends InitCommand new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), )) ->setHelp(<<getOption('no-update')) { return 0; } + $updateDevMode = $input->getOption('update-no-dev') ? false : true; // Update packages $composer = $this->getComposer(); @@ -119,7 +121,7 @@ EOT ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) ->setPreferDist($input->getOption('prefer-dist')) - ->setDevMode(true) + ->setDevMode($updateDevMode) ->setUpdate(true) ->setUpdateWhitelist(array_keys($requirements)) ->setWhitelistDependencies($input->getOption('update-with-dependencies')); From 65b9cca7b56b65185deac82bd64711753a3eece9 Mon Sep 17 00:00:00 2001 From: Denis Sokolov Date: Thu, 10 Apr 2014 13:11:48 +0200 Subject: [PATCH 215/638] Fix ClassMapGenerator unambiguousReference test coverage Without this fix the test only covers having files in "test", the "fixture" portion it is not covered at all, because all fixtures in phpunit tests are already in "test" directory --- .../Test/Autoload/ClassMapGeneratorTest.php | 31 ++++++++++++++++--- .../Test/Autoload/Fixtures/Unambiguous/A.php | 6 ---- .../Fixtures/Unambiguous/tests/FixtureA.php | 3 -- .../Fixtures/Unambiguous/tests/FixtureA2.php | 3 -- 4 files changed, 27 insertions(+), 16 deletions(-) delete mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php delete mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/tests/FixtureA.php delete mode 100644 tests/Composer/Test/Autoload/Fixtures/Unambiguous/tests/FixtureA2.php diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 570eeab10..5ca42880e 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -152,10 +152,30 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase */ public function testUnambiguousReference() { - $this->checkIfFinderIsAvailable(); + $tempDir = sys_get_temp_dir().'/ComposerTestUnambiguousRefs'; + if (!is_dir($tempDir)) { + mkdir($tempDir, 0777, true); + } - $finder = new Finder(); - $finder->files()->in(__DIR__ . '/Fixtures/Unambiguous'); + file_put_contents($tempDir.'/A.php', "getMockBuilder('Composer\IO\ConsoleIO') ->disableOriginalConstructor() @@ -164,7 +184,10 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $io->expects($this->never()) ->method('write'); - ClassMapGenerator::createMap($finder, null, $io); + ClassMapGenerator::createMap($tempDir, null, $io); + + $fs = new Filesystem(); + $fs->removeDirectory($tempDir); } /** diff --git a/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php b/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php deleted file mode 100644 index d88eba6e8..000000000 --- a/tests/Composer/Test/Autoload/Fixtures/Unambiguous/A.php +++ /dev/null @@ -1,6 +0,0 @@ - Date: Thu, 10 Apr 2014 13:15:35 +0200 Subject: [PATCH 216/638] Avoid ambiguity warning for example directories as well In particular, PEAR repositories use a classmap and example directories trigger multiple false positive warnings. --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- tests/Composer/Test/Autoload/ClassMapGeneratorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 2cdb920a8..e7547cb04 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -84,7 +84,7 @@ class ClassMapGenerator foreach ($classes as $class) { if (!isset($map[$class])) { $map[$class] = $filePath; - } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { + } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { $io->write( 'Warning: Ambiguous class resolution, "'.$class.'"'. ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.' diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 5ca42880e..1ef68d459 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -170,7 +170,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase " ); - foreach (array('test', 'fixture') as $keyword) { + foreach (array('test', 'fixture', 'example') as $keyword) { if (!is_dir($tempDir.'/'.$keyword)) { mkdir($tempDir.'/'.$keyword, 0777, true); } From d742ffca4fade100a2a9d1016e9583b1b44d85c9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 10 Apr 2014 15:55:05 +0200 Subject: [PATCH 217/638] Fix removal of missing keys --- src/Composer/Json/JsonManipulator.php | 2 ++ .../Test/Json/JsonManipulatorTest.php | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 1cb9ff79e..0ca020ad7 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -222,6 +222,8 @@ class JsonManipulator } } } + } else { + $childrenClean = $children; } // no child data left, $name was the only key in diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index f4097dcca..9b55efb2d 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -347,6 +347,27 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase } } } +' + ), + 'works on undefined ones' => array( + '{ + "repositories": { + "main": { + "foo": "bar", + "bar": "baz" + } + } +}', + 'removenotthere', + true, + '{ + "repositories": { + "main": { + "foo": "bar", + "bar": "baz" + } + } +} ' ), 'works on empty repos' => array( From 4392be4d290db356b933c6f2863d5616439bdfb7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 10 Apr 2014 16:11:43 +0200 Subject: [PATCH 218/638] Fix some more inconsistencies in json manipulation --- src/Composer/Json/JsonManipulator.php | 5 +++ .../Test/Json/JsonManipulatorTest.php | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 0ca020ad7..3fd45012c 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -204,6 +204,11 @@ class JsonManipulator list($name, $subName) = explode('.', $name, 2); } + // no node to remove + if (!isset($decoded[$mainNode][$name]) || ($subName && !isset($decoded[$mainNode][$name][$subName]))) { + return true; + } + // try and find a match for the subkey if ($this->pregMatch('{"'.preg_quote($name).'"\s*:}i', $children)) { // find best match for the value of "name" diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 9b55efb2d..31ef70f24 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -368,6 +368,50 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase } } } +' + ), + 'works on child having unmatched name' => array( + '{ + "repositories": { + "baz": { + "foo": "bar", + "bar": "baz" + } + } +}', + 'bar', + true, + '{ + "repositories": { + "baz": { + "foo": "bar", + "bar": "baz" + } + } +} +' + ), + 'works on child having duplicate name' => array( + '{ + "repositories": { + "foo": { + "baz": "qux" + }, + "baz": { + "foo": "bar", + "bar": "baz" + } + } +}', + 'baz', + true, + '{ + "repositories": { + "foo": { + "baz": "qux" + } + } +} ' ), 'works on empty repos' => array( From 556aceeaaa62822a3bccf4f1f333c4e2565de74f Mon Sep 17 00:00:00 2001 From: Jay Date: Thu, 10 Apr 2014 13:26:26 -0500 Subject: [PATCH 219/638] Removing dist reference info from Artifact Repository The getComposerInformation method adds dist information to package. This includes reference, which holds the name of the zip file. For e.g. "MyPackage-1.2.zip". Now, when using satis for hosting these packages, it uses the same code for archiving composer packages implemented at https://github.com/composer/composer/blob/master/src/Composer/Package/Archiver/ArchiveManager.php In the implementation, getPackageFilename() adds the distReference info to the file name, which makes the zip file created at the end to be weird. Like "jay-logger-1.2-MyPackage-1.2.zip.tar". I am not sure if there is any other use of dist reference in Artifact Repository, but this would stop causing that issue. --- src/Composer/Repository/ArtifactRepository.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 09efc0b9f..b4d0d8c92 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -139,7 +139,6 @@ class ArtifactRepository extends ArrayRepository $package['dist'] = array( 'type' => 'zip', 'url' => $file->getRealPath(), - 'reference' => $file->getBasename(), 'shasum' => sha1_file($file->getRealPath()) ); From 1e4df0690a08c4653b5c932d51a337b10d6c19bf Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 10 Apr 2014 12:10:45 -0700 Subject: [PATCH 220/638] Shorter boolean conversion --- src/Composer/Command/RequireCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index ee81d1ad1..9b4b50df1 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -110,7 +110,7 @@ EOT if ($input->getOption('no-update')) { return 0; } - $updateDevMode = $input->getOption('update-no-dev') ? false : true; + $updateDevMode = !$input->getOption('update-no-dev'); // Update packages $composer = $this->getComposer(); From b437c1cc052d386095a85c332536dc95366d8615 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 11 Apr 2014 14:27:14 +0200 Subject: [PATCH 221/638] Support github auth directly in the RemoteFilesystem class --- src/Composer/Command/CreateProjectCommand.php | 2 +- src/Composer/Command/DiagnoseCommand.php | 32 ++++----- src/Composer/Command/SelfUpdateCommand.php | 2 +- src/Composer/Downloader/FileDownloader.php | 68 +++++++------------ src/Composer/Factory.php | 2 +- .../Repository/ComposerRepository.php | 2 +- src/Composer/Repository/PearRepository.php | 2 +- src/Composer/Repository/Vcs/VcsDriver.php | 2 +- src/Composer/Util/GitHub.php | 2 +- src/Composer/Util/RemoteFilesystem.php | 58 ++++++++++------ .../Test/Util/RemoteFilesystemTest.php | 2 +- 11 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 643e5a24e..3cca384d2 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -232,7 +232,7 @@ EOT if (null === $repositoryUrl) { $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { - $sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io))); + $sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config))); } elseif (0 === strpos($repositoryUrl, 'http')) { $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); } else { diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 445bcc1aa..21523a062 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -48,7 +48,22 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $this->rfs = new RemoteFilesystem($this->getIO()); + $composer = $this->getComposer(false); + if ($composer) { + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + + $output->write('Checking composer.json: '); + $this->outputResult($output, $this->checkComposerSchema()); + } + + if ($composer) { + $config = $composer->getConfig(); + } else { + $config = Factory::createConfig(); + } + + $this->rfs = new RemoteFilesystem($this->getIO(), $config); $this->process = new ProcessExecutor($this->getIO()); $output->write('Checking platform settings: '); @@ -70,21 +85,6 @@ EOT $this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam()); } - $composer = $this->getComposer(false); - if ($composer) { - $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output); - $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - - $output->write('Checking composer.json: '); - $this->outputResult($output, $this->checkComposerSchema()); - } - - if ($composer) { - $config = $composer->getConfig(); - } else { - $config = Factory::createConfig(); - } - if ($oauth = $config->get('github-oauth')) { foreach ($oauth as $domain => $token) { $output->write('Checking '.$domain.' oauth access: '); diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 3782a5c62..a55b9b372 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -57,8 +57,8 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE; - $remoteFilesystem = new RemoteFilesystem($this->getIO()); $config = Factory::createConfig(); + $remoteFilesystem = new RemoteFilesystem($this->getIO(), $config); $cacheDir = $config->get('cache-dir'); $rollbackDir = $config->get('home'); $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0]; diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 2f8c048dc..5ff27d18f 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -56,7 +56,7 @@ class FileDownloader implements DownloaderInterface $this->io = $io; $this->config = $config; $this->eventDispatcher = $eventDispatcher; - $this->rfs = $rfs ?: new RemoteFilesystem($io); + $this->rfs = $rfs ?: new RemoteFilesystem($io, $config); $this->filesystem = $filesystem ?: new Filesystem(); $this->cache = $cache; @@ -108,53 +108,35 @@ class FileDownloader implements DownloaderInterface $checksum = $package->getDistSha1Checksum(); $cacheKey = $this->getCacheKey($package); - try { - // download if we don't have it in cache or the cache is invalidated - if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) { - if (!$this->outputProgress) { - $this->io->write(' Downloading'); - } + // download if we don't have it in cache or the cache is invalidated + if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) { + if (!$this->outputProgress) { + $this->io->write(' Downloading'); + } - // try to download 3 times then fail hard - $retries = 3; - while ($retries--) { - try { - $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); - break; - } catch (TransportException $e) { - // if we got an http response with a proper code, then requesting again will probably not help, abort - if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) { - throw $e; - } - if ($this->io->isVerbose()) { - $this->io->write(' Download failed, retrying...'); - } - usleep(500000); + // try to download 3 times then fail hard + $retries = 3; + while ($retries--) { + try { + $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); + break; + } catch (TransportException $e) { + // if we got an http response with a proper code, then requesting again will probably not help, abort + if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) { + throw $e; } + if ($this->io->isVerbose()) { + $this->io->write(' Download failed, retrying...'); + } + usleep(500000); } + } - if ($this->cache) { - $this->cache->copyFrom($cacheKey, $fileName); - } - } else { - $this->io->write(' Loading from cache'); - } - } catch (TransportException $e) { - if (!in_array($e->getCode(), array(404, 403, 412))) { - throw $e; - } - if ('github.com' === $hostname && !$this->io->hasAuthentication($hostname)) { - $message = "\n".'Could not fetch '.$processedUrl.', enter your GitHub credentials '.($e->getCode() === 404 ? 'to access private repos' : 'to go over the API rate limit'); - $gitHubUtil = new GitHub($this->io, $this->config, null, $rfs); - if (!$gitHubUtil->authorizeOAuth($hostname) - && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($hostname, $message)) - ) { - throw $e; - } - $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress); - } else { - throw $e; + if ($this->cache) { + $this->cache->copyFrom($cacheKey, $fileName); } + } else { + $this->io->write(' Loading from cache'); } if (!file_exists($fileName)) { diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 88fc4132e..b1bdd9599 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -275,7 +275,7 @@ class Factory $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock'; - $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, $im, md5_file($composerFile)); + $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile)); $composer->setLocker($locker); } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 1d686cae1..37f6d810b 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $this->io = $io; $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$'); $this->loader = new ArrayLoader(); - $this->rfs = new RemoteFilesystem($this->io, $this->options); + $this->rfs = new RemoteFilesystem($this->io, $this->config, $this->options); $this->eventDispatcher = $eventDispatcher; } diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index a106385a5..5006c4927 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -57,7 +57,7 @@ class PearRepository extends ArrayRepository $this->url = rtrim($repoConfig['url'], '/'); $this->io = $io; - $this->rfs = $rfs ?: new RemoteFilesystem($this->io); + $this->rfs = $rfs ?: new RemoteFilesystem($this->io, $config); $this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null; $this->versionParser = new VersionParser(); } diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index f1112074e..04956e816 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -57,7 +57,7 @@ abstract class VcsDriver implements VcsDriverInterface $this->io = $io; $this->config = $config; $this->process = $process ?: new ProcessExecutor($io); - $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); + $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config); } /** diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index a0b2b7460..69e3b46bf 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -40,7 +40,7 @@ class GitHub $this->io = $io; $this->config = $config; $this->process = $process ?: new ProcessExecutor; - $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); + $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config); } /** diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index fd5d431c6..3272c4cde 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -13,6 +13,7 @@ namespace Composer\Util; use Composer\Composer; +use Composer\Config; use Composer\IO\IOInterface; use Composer\Downloader\TransportException; @@ -24,6 +25,7 @@ use Composer\Downloader\TransportException; class RemoteFilesystem { private $io; + private $config; private $firstCall; private $bytesMax; private $originUrl; @@ -41,9 +43,10 @@ class RemoteFilesystem * @param IOInterface $io The IO instance * @param array $options The options */ - public function __construct(IOInterface $io, $options = array()) + public function __construct(IOInterface $io, Config $config = null, array $options = array()) { $this->io = $io; + $this->config = $config; $this->options = $options; } @@ -267,26 +270,14 @@ class RemoteFilesystem break; } - if (!$this->io->isInteractive()) { - $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; - - throw new TransportException($message, 401); - } - - $this->promptAuthAndRetry(); + $this->promptAuthAndRetry($messageCode); break; } break; case STREAM_NOTIFY_AUTH_RESULT: if (403 === $messageCode) { - if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) { - $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $message; - - throw new TransportException($message, 403); - } - - $this->promptAuthAndRetry(); + $this->promptAuthAndRetry($messageCode, $message); break; } break; @@ -317,12 +308,39 @@ class RemoteFilesystem } } - protected function promptAuthAndRetry() + protected function promptAuthAndRetry($httpStatus, $reason) { - $this->io->overwrite(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); - $username = $this->io->ask(' Username: '); - $password = $this->io->askAndHideAnswer(' Password: '); - $this->io->setAuthentication($this->originUrl, $username, $password); + if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { + $message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); + $gitHubUtil = new GitHub($this->io, $this->config, null, $this); + if (!$gitHubUtil->authorizeOAuth($this->originUrl) + && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message)) + ) { + throw new TransportException('Could not authenticate against '.$this->originUrl); + } + } else { + // 404s are only handled for github + if ($httpStatus === 404) { + return; + } + + // fail if we already have auth or the console is not interactive + if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) { + if ($httpStatus === 401) { + $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate"; + } + if ($httpStatus === 403) { + $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason; + } + + throw new TransportException($message, $httpStatus); + } + + $this->io->overwrite(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); + $username = $this->io->ask(' Username: '); + $password = $this->io->askAndHideAnswer(' Password: '); + $this->io->setAuthentication($this->originUrl, $username, $password); + } $this->retry = true; throw new TransportException('RETRY'); diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index 92aa60630..bbeba908e 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -173,7 +173,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase protected function callGetOptionsForUrl($io, array $args = array(), array $options = array()) { - $fs = new RemoteFilesystem($io, $options); + $fs = new RemoteFilesystem($io, null, $options); $ref = new \ReflectionMethod($fs, 'getOptionsForUrl'); $ref->setAccessible(true); From f3c112e9e022141dda4c2b312ab2119e4e5805a6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 11 Apr 2014 15:01:20 +0200 Subject: [PATCH 222/638] Split non-interactive and bad credential cases --- src/Composer/Util/RemoteFilesystem.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 3272c4cde..0dda89a99 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -324,8 +324,8 @@ class RemoteFilesystem return; } - // fail if we already have auth or the console is not interactive - if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) { + // fail if the console is not interactive + if (!$this->io->isInteractive()) { if ($httpStatus === 401) { $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate"; } @@ -335,6 +335,10 @@ class RemoteFilesystem throw new TransportException($message, $httpStatus); } + // fail if we already have auth + if ($this->io->hasAuthentication($this->originUrl)) { + throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); + } $this->io->overwrite(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); $username = $this->io->ask(' Username: '); From 57d9e9852d14756647dbb998105b45c19c36fe78 Mon Sep 17 00:00:00 2001 From: Cullen Walsh Date: Mon, 14 Apr 2014 00:21:53 -0400 Subject: [PATCH 223/638] Support generating class map for XHP classes --- src/Composer/Autoload/ClassMapGenerator.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e7547cb04..469cc9638 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -136,7 +136,7 @@ class ClassMapGenerator preg_match_all('{ (?: - \b(?])(?Pclass|interface'.$traits.') \s+ (?P[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*) + \b(?])(?Pclass|interface'.$traits.') \s+ (?P[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:]*) | \b(?])(?Pnamespace) (?P\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;] ) }ix', $contents, $matches); @@ -148,7 +148,12 @@ class ClassMapGenerator if (!empty($matches['ns'][$i])) { $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\'; } else { - $classes[] = ltrim($namespace . $matches['name'][$i], '\\'); + $name = $matches['name'][$i]; + if ($name[0] === ':') { + // This is an XHP class, https://github.com/facebook/xhp + $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); + } + $classes[] = ltrim($namespace . $name, '\\'); } } From 49d4054e51d283290af3cf6bf5dbd517d879096b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 14 Apr 2014 12:47:47 +0200 Subject: [PATCH 224/638] Handle files with no php at all, fixes #2910 --- src/Composer/Autoload/ClassMapGenerator.php | 5 ++++- .../Test/Autoload/Fixtures/template/notphp.inc | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/template/notphp.inc diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e7547cb04..f98543f88 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -124,7 +124,10 @@ class ClassMapGenerator $contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}s', 'null', $contents); // strip leading non-php code if needed if (substr($contents, 0, 2) !== '.+<\?}s', '?> array +( + 'handler' => array ('midgard_admin_asgard_handler_preferences', 'ajax'), + 'fixed_args' => array('preferences', 'ajax'), + 'variable_args' => 0, +), From 1347d5306a04c4dd6f957fa5cd326b07097c5ed3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 14 Apr 2014 13:55:17 +0200 Subject: [PATCH 225/638] Make sure a good timezone is set, fixes #2899 --- tests/bootstrap.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9e59c65a7..908861cf5 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,5 +12,9 @@ error_reporting(E_ALL); +if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) { + date_default_timezone_set(@date_default_timezone_get()); +} + require __DIR__.'/../src/bootstrap.php'; require __DIR__.'/Composer/TestCase.php'; From ee6a843b8ea77d1eb2cecda0a18fe4921e28f2a6 Mon Sep 17 00:00:00 2001 From: Evan-R Date: Tue, 15 Apr 2014 04:58:44 -0600 Subject: [PATCH 226/638] add note for alternate download method in case curl fails --- doc/00-intro.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/00-intro.md b/doc/00-intro.md index 451e9c359..fbb8c3613 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -64,6 +64,11 @@ To actually get Composer, we need to do two things. The first one is installing Composer (again, this means downloading it into your project): $ curl -sS https://getcomposer.org/installer | php + +> **Note:** If the above fails for some reason, you can download the installer +> with `php` instead: + + $ php -r "readfile('https://getcomposer.org/installer');" | php This will just check a few PHP settings and then download `composer.phar` to your working directory. This file is the Composer binary. It is a PHAR (PHP From 8dd6b052ce3ba86cd31894e139275a6490b360e6 Mon Sep 17 00:00:00 2001 From: schmkr Date: Tue, 15 Apr 2014 23:56:45 +0200 Subject: [PATCH 227/638] Updating SvnDriver trunk revision check Not checking revision of composer.json, but from its parent directory. See composer/composer#2849 --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 716f5a14d..506c176b8 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -207,7 +207,7 @@ class SvnDriver extends VcsDriver foreach ($this->process->splitLines($output) as $line) { $line = trim($line); if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { - if (isset($match[1]) && isset($match[2]) && $match[2] === 'composer.json') { + if (isset($match[1]) && isset($match[2]) && $match[2] === './') { $this->branches['trunk'] = $this->buildIdentifier( '/' . $this->trunkPath, $match[1] From 3251f9f1aa997479fff78905841e22beacfbe37b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 16 Apr 2014 10:23:45 +0200 Subject: [PATCH 228/638] Only check phar validity if phar.readonly is false --- src/Composer/Command/SelfUpdateCommand.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index a55b9b372..8ce355d9c 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -174,17 +174,18 @@ EOT { try { @chmod($newFilename, 0777 & ~umask()); - // test the phar validity - $phar = new \Phar($newFilename); - // free the variable to unlock the file - unset($phar); + if (!ini_get('phar.readonly')) { + // test the phar validity + $phar = new \Phar($newFilename); + // free the variable to unlock the file + unset($phar); + } // copy current file into installations dir if ($backupTarget && file_exists($localFilename)) { @copy($localFilename, $backupTarget); } - unset($phar); rename($newFilename, $localFilename); } catch (\Exception $e) { if ($backupTarget) { From cfed93287c289c2f829fbc52a4a9e1b66a215b6f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 16 Apr 2014 17:23:42 +0200 Subject: [PATCH 229/638] Remove fenced block --- doc/articles/handling-private-packages-with-satis.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 0219f8108..01fe63494 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -178,11 +178,10 @@ It is possible to make satis automatically resolve and add all dependencies for with the Downloads functionality to have a complete local mirror of packages. Just add the following to your `satis.json`: -``` -{ - "require-dependencies": true -} -``` + + { + "require-dependencies": true + } When searching for packages, satis will attempt to resolve all the required packages from the listed repositories. Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`. From 009fcb4262154c8ff926dcd42e4621c250927884 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Apr 2014 19:34:54 +0200 Subject: [PATCH 230/638] Fix arg signature --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 0dda89a99..106f61285 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -308,7 +308,7 @@ class RemoteFilesystem } } - protected function promptAuthAndRetry($httpStatus, $reason) + protected function promptAuthAndRetry($httpStatus, $reason = null) { if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { $message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); From eebffacd9f54c4527448434329765f845e8ee3ed Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 24 Apr 2014 16:41:42 +0200 Subject: [PATCH 231/638] Use pagination to get tags/branches from the github API --- src/Composer/Repository/Vcs/GitHubDriver.php | 47 +++++++++++++++----- src/Composer/Util/RemoteFilesystem.php | 16 +++++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 38c7869a5..0dc8a7eee 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -198,12 +198,17 @@ class GitHubDriver extends VcsDriver return $this->gitDriver->getTags(); } if (null === $this->tags) { - $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags'; - $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); $this->tags = array(); - foreach ($tagsData as $tag) { - $this->tags[$tag['name']] = $tag['commit']['sha']; - } + $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags?per_page=100'; + + do { + $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); + foreach ($tagsData as $tag) { + $this->tags[$tag['name']] = $tag['commit']['sha']; + } + + $resource = $this->getNextPage(); + } while ($resource); } return $this->tags; @@ -218,13 +223,18 @@ class GitHubDriver extends VcsDriver return $this->gitDriver->getBranches(); } if (null === $this->branches) { - $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads'; - $branchData = JsonFile::parseJson($this->getContents($resource), $resource); $this->branches = array(); - foreach ($branchData as $branch) { - $name = substr($branch['ref'], 11); - $this->branches[$name] = $branch['object']['sha']; - } + $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100'; + + do { + $branchData = JsonFile::parseJson($this->getContents($resource), $resource); + foreach ($branchData as $branch) { + $name = substr($branch['ref'], 11); + $this->branches[$name] = $branch['object']['sha']; + } + + $resource = $this->getNextPage(); + } while ($resource); } return $this->branches; @@ -428,4 +438,19 @@ class GitHubDriver extends VcsDriver ); $this->gitDriver->initialize(); } + + protected function getNextPage() + { + $headers = $this->remoteFilesystem->getLastHeaders(); + foreach ($headers as $header) { + if (substr($header, 0, 5) === 'Link:') { + $links = explode(',', substr($header, 5)); + foreach ($links as $link) { + if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) { + return $match[1]; + } + } + } + } + } } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 106f61285..2f9dacf69 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -36,6 +36,7 @@ class RemoteFilesystem private $lastProgress; private $options; private $retryAuthFailure; + private $lastHeaders; /** * Constructor. @@ -91,6 +92,16 @@ class RemoteFilesystem return $this->options; } + /** + * Returns the headers of the last request + * + * @return array + */ + public function getLastHeaders() + { + return $this->lastHeaders; + } + /** * Get file content or copy action. * @@ -114,6 +125,7 @@ class RemoteFilesystem $this->progress = $progress; $this->lastProgress = null; $this->retryAuthFailure = true; + $this->lastHeaders = array(); // capture username/password from URL if there is one if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) { @@ -245,6 +257,10 @@ class RemoteFilesystem throw $e; } + if (!empty($http_response_header[0])) { + $this->lastHeaders = $http_response_header; + } + return $result; } From 2c6195785dd2f10abcf0ebaaf9e2564b3e705520 Mon Sep 17 00:00:00 2001 From: Toni Uebernickel Date: Sun, 27 Apr 2014 12:29:35 +0200 Subject: [PATCH 232/638] fix autoload-dev example for PSR-4 The example leads to the following error. ``` [InvalidArgumentException] A non-empty PSR-4 prefix must end with a namespace separator. ``` --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 071b3bd9f..906075372 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -534,7 +534,7 @@ Example: "psr-4": { "MyLibrary\\": "src/" } }, "autoload-dev": { - "psr-4": { "MyLibrary\\Tests": "tests/" } + "psr-4": { "MyLibrary\\Tests\\": "tests/" } } } From 12421dd6b7eeaa1cb9b62754ce84ff6e1de94441 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 28 Apr 2014 15:19:38 +0200 Subject: [PATCH 233/638] fixed class map generator when using a heredoc with spaces --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- .../Autoload/Fixtures/classmap/StripNoise.php | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 5d939ffa9..924a930c7 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -119,7 +119,7 @@ class ClassMapGenerator } // strip heredocs/nowdocs - $contents = preg_replace('{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s', 'null', $contents); + $contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents); // strip strings $contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}s', 'null', $contents); // strip leading non-php code if needed diff --git a/tests/Composer/Test/Autoload/Fixtures/classmap/StripNoise.php b/tests/Composer/Test/Autoload/Fixtures/classmap/StripNoise.php index 3fe5389be..4c344089b 100644 --- a/tests/Composer/Test/Autoload/Fixtures/classmap/StripNoise.php +++ b/tests/Composer/Test/Autoload/Fixtures/classmap/StripNoise.php @@ -15,12 +15,30 @@ class Fail2 } A -. <<<'TEST' +. <<< AB class Fail3 { } -TEST; +AB +. <<<'TEST' +class Fail4 +{ + +} +TEST +. <<< 'ANOTHER' +class Fail5 +{ + +} +ANOTHER +. <<< 'ONEMORE' +class Fail6 +{ + +} +ONEMORE; } public function test2() From 05d9912f97a2decf6a5c08dfa569dcf23d79b16d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 29 Apr 2014 14:35:02 +0200 Subject: [PATCH 234/638] Ignore classes in ClassMapGenerator --- src/Composer/Autoload/AutoloadGenerator.php | 12 ++++++------ src/Composer/Autoload/ClassMapGenerator.php | 11 +++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 20f8c7570..1f446c58b 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -184,12 +184,12 @@ EOF; preg_quote($dir), ($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : '' ); - foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io) as $class => $path) { - if ('' === $namespace || 0 === strpos($class, $namespace)) { - if (!isset($classMap[$class])) { - $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); - $classMap[$class] = $path.",\n"; - } + + $namespaceFilter = $namespace === '' ? null : $namespace; + foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io, $namespaceFilter) as $class => $path) { + if (!isset($classMap[$class])) { + $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); + $classMap[$class] = $path.",\n"; } } } diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 924a930c7..7b977cd3d 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -45,13 +45,15 @@ class ClassMapGenerator * Iterate over all files in the given directory searching for classes * * @param \Iterator|string $path The path to search in or an iterator - * @param string $whitelist Regex that matches against the file path + * @param string $whitelist Regex that matches against the file path + * @param IOInterface $io IO object + * @param string $namespace Optional namespace prefix to filter by * * @return array A class map array * * @throws \RuntimeException When the path is neither an existing file nor directory */ - public static function createMap($path, $whitelist = null, IOInterface $io = null) + public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null) { if (is_string($path)) { if (is_file($path)) { @@ -82,6 +84,11 @@ class ClassMapGenerator $classes = self::findClasses($filePath); foreach ($classes as $class) { + // skip classes not within the given namespace prefix + if (null !== $namespace && 0 !== strpos($class, $namespace)) { + continue; + } + if (!isset($map[$class])) { $map[$class] = $filePath; } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { From 8fd82784b2191bc5fd76fad79f456c46bfac3b8e Mon Sep 17 00:00:00 2001 From: jakoch Date: Wed, 30 Apr 2014 11:29:52 +0200 Subject: [PATCH 235/638] disable these xdebug settings only, if xdebug is loaded --- src/Composer/Console/Application.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 9d622cf67..da5d4ff20 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -56,11 +56,11 @@ class Application extends BaseApplication public function __construct() { - if (function_exists('ini_set')) { + if (function_exists('ini_set') && extension_loaded('xdebug')) { ini_set('xdebug.show_exception_trace', false); ini_set('xdebug.scream', false); - } + if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) { date_default_timezone_set(@date_default_timezone_get()); } From 01968efa6f61ba6027fdca701ffa5300ef067586 Mon Sep 17 00:00:00 2001 From: jakoch Date: Wed, 30 Apr 2014 11:52:31 +0200 Subject: [PATCH 236/638] renamed listFiles() to getFolderContent(). fixed comment: the method doesn't return a "list of files in a directory, including dotfiles", it returns a "list of files and folders, excluding dotfiles". switched from !is_file() to is_dir() check. --- src/Composer/Downloader/ArchiveDownloader.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index bfe174fb7..bea491a02 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -49,12 +49,11 @@ abstract class ArchiveDownloader extends FileDownloader unlink($fileName); - // get file list - $contentDir = $this->listFiles($temporaryDir); + $contentDir = $this->getFolderContent($temporaryDir); // only one dir in the archive, extract its contents out of it - if (1 === count($contentDir) && !is_file($contentDir[0])) { - $contentDir = $this->listFiles($contentDir[0]); + if (1 === count($contentDir) && is_dir($contentDir[0])) { + $contentDir = $this->getFolderContent($contentDir[0]); } // move files back out of the temp dir @@ -128,9 +127,11 @@ abstract class ArchiveDownloader extends FileDownloader abstract protected function extract($file, $path); /** - * Returns the list of files in a directory including dotfiles + * Returns the folder content, excluding dotfiles + * + * @param string $dir Directory */ - private function listFiles($dir) + private function getFolderContent($dir) { $files = array_merge(glob($dir . '/.*') ?: array(), glob($dir . '/*') ?: array()); From 4f417e3985a512c8793ac930ec29c7f609c69412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Macias?= Date: Wed, 30 Apr 2014 15:51:54 +0200 Subject: [PATCH 237/638] Add --no-check-all option to composer validate command --- doc/03-cli.md | 4 ++++ src/Composer/Command/ValidateCommand.php | 8 ++++++-- src/Composer/Util/ConfigValidator.php | 5 +++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index e885a7a9f..acbd470e2 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -248,6 +248,10 @@ You should always run the `validate` command before you commit your $ php composer.phar validate +### Options + +* **--no-check-all:** Wether or not composer do a complete validation. + ## status If you often need to modify the code of your dependencies and they are diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 38a524125..e7e0860e1 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -12,9 +12,11 @@ namespace Composer\Command; +use Composer\Package\Loader\ValidatingArrayLoader; use Composer\Util\ConfigValidator; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -34,6 +36,7 @@ class ValidateCommand extends Command ->setName('validate') ->setDescription('Validates a composer.json') ->setDefinition(array( + new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'), new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json') )) ->setHelp(<<getIO()); - list($errors, $publishErrors, $warnings) = $validator->validate($file); + $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL; + list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); // output errors/warnings if (!$errors && !$publishErrors && !$warnings) { diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 731575e90..5b9a7cf26 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -38,10 +38,11 @@ class ConfigValidator * Validates the config, and returns the result. * * @param string $file The path to the file + * @param integer $arrayLoaderValidationFlags Flags for ArrayLoader validation * * @return array a triple containing the errors, publishable errors, and warnings */ - public function validate($file) + public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL) { $errors = array(); $publishErrors = array(); @@ -119,7 +120,7 @@ class ConfigValidator } try { - $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, ValidatingArrayLoader::CHECK_ALL); + $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, $arrayLoaderValidationFlags); if (!isset($manifest['version'])) { $manifest['version'] = '1.0.0'; } From a0e5ead9b0b116f20917d9d5b7b9eeefe830af80 Mon Sep 17 00:00:00 2001 From: jakoch Date: Fri, 2 May 2014 17:42:31 +0200 Subject: [PATCH 238/638] removed unused variables $baseDir and $vendorDir from "autoloader_real.php" generated by AutoloadGenerator. adjusted tests accordingly. --- src/Composer/Autoload/AutoloadGenerator.php | 3 --- .../Autoload/Fixtures/autoload_real_files_by_dependency.php | 3 --- .../Test/Autoload/Fixtures/autoload_real_functions.php | 3 --- .../Test/Autoload/Fixtures/autoload_real_include_path.php | 3 --- .../Test/Autoload/Fixtures/autoload_real_target_dir.php | 3 --- 5 files changed, 15 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 1f446c58b..e2eb6bf56 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -474,9 +474,6 @@ class ComposerAutoloaderInit$suffix self::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader')); - \$vendorDir = $vendorPathCode; - \$baseDir = $appBaseDirCode; - HEADER; diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php index ad8591237..083070539 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php @@ -23,9 +23,6 @@ class ComposerAutoloaderInitFilesAutoloadOrder self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader')); - $vendorDir = dirname(__DIR__); - $baseDir = dirname($vendorDir); - $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 4c44366b3..1c0154964 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -23,9 +23,6 @@ class ComposerAutoloaderInitFilesAutoload self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); - $vendorDir = dirname(__DIR__); - $baseDir = dirname($vendorDir); - $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php index aae480897..65ba6819e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -23,9 +23,6 @@ class ComposerAutoloaderInitIncludePath self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader')); - $vendorDir = dirname(__DIR__); - $baseDir = dirname($vendorDir); - $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php index 22d7d1653..dc786f767 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -23,9 +23,6 @@ class ComposerAutoloaderInitTargetDir self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader')); - $vendorDir = dirname(__DIR__); - $baseDir = dirname($vendorDir); - $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); From 2a3159ec577f97408c1e1c28c35335faa6448039 Mon Sep 17 00:00:00 2001 From: jakoch Date: Wed, 30 Apr 2014 12:06:02 +0200 Subject: [PATCH 239/638] reduce commit history of git checkout (git depth 5) --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index a2803f0e6..b019c1926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,3 +22,6 @@ before_script: script: - ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml {};' || exit 1 + +git: + depth: 5 \ No newline at end of file From ee142688e1e2d88bfbc8e5ef80412b07befb263d Mon Sep 17 00:00:00 2001 From: renatbilalov Date: Wed, 7 May 2014 14:55:42 +0600 Subject: [PATCH 240/638] Corrected description of the option --optimize (-o) --- src/Composer/Command/DumpAutoloadCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 86d351505..b30fbd140 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -30,7 +30,7 @@ class DumpAutoloadCommand extends Command ->setAliases(array('dumpautoload')) ->setDescription('Dumps the autoloader') ->setDefinition(array( - new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 packages to be loaded with classmaps too, good for production.'), + new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), )) ->setHelp(<< Date: Wed, 7 May 2014 17:45:37 +0200 Subject: [PATCH 241/638] Updated Satis install command. --- doc/articles/handling-private-packages-with-satis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 01fe63494..fda8b38fa 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -9,7 +9,7 @@ lightweight, static file-based version of packagist and can be used to host the metadata of your company's private packages, or your own. It basically acts as a micro-packagist. You can get it from [GitHub](http://github.com/composer/satis) or install via CLI: -`composer.phar create-project composer/satis --stability=dev`. +`php composer.phar create-project composer/satis --stability=dev --keep-vcs`. ## Setup From 31092843fd52497c888563f1fe86641ad36ea1eb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 18:12:26 +0200 Subject: [PATCH 242/638] Only load options when they come from the lock file, not from standard metadata, refs #2189 --- src/Composer/Package/Loader/ArrayLoader.php | 6 ++++-- src/Composer/Package/Locker.php | 2 +- tests/Composer/Test/Package/Loader/ArrayLoaderTest.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 23c305d0d..d96de2fa9 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -25,13 +25,15 @@ use Composer\Package\Version\VersionParser; class ArrayLoader implements LoaderInterface { protected $versionParser; + protected $loadOptions; - public function __construct(VersionParser $parser = null) + public function __construct(VersionParser $parser = null, $loadOptions = false) { if (!$parser) { $parser = new VersionParser; } $this->versionParser = $parser; + $this->loadOptions = $loadOptions; } public function load(array $config, $class = 'Composer\Package\CompletePackage') @@ -197,7 +199,7 @@ class ArrayLoader implements LoaderInterface } } - if (isset($config['options'])) { + if ($this->loadOptions && isset($config['options'])) { $package->setOptions($config['options']); } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index a928584e4..ea5297cc3 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -56,7 +56,7 @@ class Locker $this->repositoryManager = $repositoryManager; $this->installationManager = $installationManager; $this->hash = $hash; - $this->loader = new ArrayLoader(); + $this->loader = new ArrayLoader(null, true); $this->dumper = new ArrayDumper(); $this->process = new ProcessExecutor($io); } diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 474929307..593461fb7 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -19,7 +19,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase { public function setUp() { - $this->loader = new ArrayLoader(); + $this->loader = new ArrayLoader(null, true); } public function testSelfVersion() From 32cd883daaa0e396a2f6385c212cdcef1b02042e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 18:25:28 +0200 Subject: [PATCH 243/638] Rename options to transport-options, refs #2189 --- src/Composer/Downloader/FileDownloader.php | 2 +- src/Composer/Package/BasePackage.php | 16 +++++++++------- src/Composer/Package/Dumper/ArrayDumper.php | 4 ++-- src/Composer/Package/Loader/ArrayLoader.php | 4 ++-- .../Package/Loader/ValidatingArrayLoader.php | 2 +- src/Composer/Package/PackageInterface.php | 9 +-------- src/Composer/Repository/ComposerRepository.php | 2 +- .../Test/Downloader/FileDownloaderTest.php | 4 ++-- .../Test/Downloader/ZipDownloaderTest.php | 2 +- .../Test/Package/Dumper/ArrayDumperTest.php | 5 +++-- .../Test/Package/Loader/ArrayLoaderTest.php | 2 +- .../Package/Loader/ValidatingArrayLoaderTest.php | 6 +++--- 12 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 3e2a2972d..8c98eb785 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -118,7 +118,7 @@ class FileDownloader implements DownloaderInterface $retries = 3; while ($retries--) { try { - $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions()); + $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions()); break; } catch (TransportException $e) { // if we got an http response with a proper code, then requesting again will probably not help, abort diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index afc20f4b6..d756196ef 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -49,7 +49,7 @@ abstract class BasePackage implements PackageInterface protected $repository; protected $id; - protected $options; + protected $transportOptions; /** * All descendants' constructors should call this parent constructor @@ -61,7 +61,7 @@ abstract class BasePackage implements PackageInterface $this->prettyName = $name; $this->name = strtolower($name); $this->id = -1; - $this->options = array(); + $this->transportOptions = array(); } /** @@ -138,17 +138,19 @@ abstract class BasePackage implements PackageInterface /** * {@inheritDoc} */ - public function getOptions() + public function getTransportOptions() { - return $this->options; + return $this->transportOptions; } /** - * {@inheritDoc} + * Configures the list of options to download package dist files + * + * @param array $options */ - public function setOptions(array $options) + public function setTransportOptions(array $options) { - $this->options = $options; + $this->transportOptions = $options; } /** diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index b81912837..8741acefb 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -108,8 +108,8 @@ class ArrayDumper } } - if (count($package->getOptions()) > 0) { - $data['options'] = $package->getOptions(); + if (count($package->getTransportOptions()) > 0) { + $data['transport-options'] = $package->getTransportOptions(); } return $data; diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index d96de2fa9..66d48f65e 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -199,8 +199,8 @@ class ArrayLoader implements LoaderInterface } } - if ($this->loadOptions && isset($config['options'])) { - $package->setOptions($config['options']); + if ($this->loadOptions && isset($config['transport-options'])) { + $package->setTransportOptions($config['transport-options']); } return $package; diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index ac8010459..aef24f891 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -230,7 +230,7 @@ class ValidatingArrayLoader implements LoaderInterface // TODO validate package repositories' packages using this recursively $this->validateFlatArray('include-path'); - $this->validateArray('options'); + $this->validateArray('transport-options'); // branch alias validation if (isset($this->config['extra']['branch-alias'])) { diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index ea3a86420..337a72a83 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -314,17 +314,10 @@ interface PackageInterface */ public function getArchiveExcludes(); - /** - * Configures the list of options to download package dist files - * - * @param array $options - */ - public function setOptions(array $options); - /** * Returns a list of options to download package dist files * * @return array */ - public function getOptions(); + public function getTransportOptions(); } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 529128dd7..63b3dbf86 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -219,7 +219,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository { if ($package instanceof Package && strpos($package->getDistUrl(), $this->baseUrl) === 0) { - $package->setOptions($this->options); + $package->setTransportOptions($this->options); } } diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php index 9fb34b846..28714de4a 100644 --- a/tests/Composer/Test/Downloader/FileDownloaderTest.php +++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php @@ -90,7 +90,7 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('http://example.com/script.js')) ; $packageMock->expects($this->atLeastOnce()) - ->method('getOptions') + ->method('getTransportOptions') ->will($this->returnValue(array())) ; @@ -166,7 +166,7 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('http://example.com/script.js')) ; $packageMock->expects($this->atLeastOnce()) - ->method('getOptions') + ->method('getTransportOptions') ->will($this->returnValue(array())) ; $packageMock->expects($this->any()) diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php index 441777d8c..36b2c2296 100644 --- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php @@ -31,7 +31,7 @@ class ZipDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('file://'.__FILE__)) ; $packageMock->expects($this->atLeastOnce()) - ->method('getOptions') + ->method('getTransportOptions') ->will($this->returnValue(array())) ; diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 54e8de9cc..5576e3d05 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -196,8 +196,9 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase array('bar/baz' => '1.0.0', 'foo/bar' => '1.0.0') ), array( - 'options', - array('ssl' => array('local_cert' => '/opt/certs/test.pem')) + 'transport-options', + array('ssl' => array('local_cert' => '/opt/certs/test.pem')), + 'transportOptions' ) ); } diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 593461fb7..04a537abd 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -117,7 +117,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase 'archive' => array( 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), ), - 'options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) + 'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) ); $package = $this->loader->load($config); diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 45e3f454c..593598a58 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -146,7 +146,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'bin/foo', 'bin/bar', ), - 'options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) + 'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) ), ), array( // test as array @@ -267,10 +267,10 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase array( array( 'name' => 'foo/bar', - 'options' => 'test', + 'transport-options' => 'test', ), array( - 'options : should be an array, string given' + 'transport-options : should be an array, string given' ) ), ); From faeb706de6fbcd3e75f2ddcd2b14af97eccd5d9b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 19:10:55 +0200 Subject: [PATCH 244/638] Handle alias packages properly, refs #2189 --- src/Composer/Package/AliasPackage.php | 8 ++++++++ src/Composer/Repository/ComposerRepository.php | 14 ++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 631b035a7..c90221027 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -233,6 +233,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getDistSha1Checksum(); } + public function setTransportOptions(array $options) + { + return $this->aliasOf->setTransportOptions($options); + } + public function getTransportOptions() + { + return $this->aliasOf->getTransportOptions(); + } public function getScripts() { return $this->aliasOf->getScripts(); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 63b3dbf86..8ed6897c4 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -210,16 +210,15 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $package = $package->getAliasOf(); } $package->setRepository($this); - $this->configurePackageOptions($package); return $package; } - protected function configurePackageOptions(PackageInterface $package) + protected function configurePackageTransportOptions(PackageInterface $package) { - if ($package instanceof Package - && strpos($package->getDistUrl(), $this->baseUrl) === 0) { + if (strpos($package->getDistUrl(), $this->baseUrl) === 0) { $package->setTransportOptions($this->options); + return; } } @@ -399,7 +398,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository public function addPackage(PackageInterface $package) { parent::addPackage($package); - $this->configurePackageOptions($package); + $this->configurePackageTransportOptions($package); } protected function loadRootServerFile() @@ -548,7 +547,10 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $data['notification-url'] = $this->notifyUrl; } - return $this->loader->load($data, 'Composer\Package\CompletePackage'); + $package = $this->loader->load($data, 'Composer\Package\CompletePackage'); + $this->configurePackageTransportOptions($package); + + return $package; } catch (\Exception $e) { throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e); } From e707dcd92f63236124ddb00eb625f53a2299bf07 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 19:29:59 +0200 Subject: [PATCH 245/638] Also load transport options in filesystem repositories to avoid loss from the installed repo, refs #2189 --- src/Composer/Repository/FilesystemRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index f6ea19ab7..0ffac9f1d 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -57,7 +57,7 @@ class FilesystemRepository extends WritableArrayRepository throw new InvalidRepositoryException('Invalid repository data in '.$this->file->getPath().', packages could not be loaded: ['.get_class($e).'] '.$e->getMessage()); } - $loader = new ArrayLoader(); + $loader = new ArrayLoader(null, true); foreach ($packages as $packageData) { $package = $loader->load($packageData); $this->addPackage($package); From 77163f66fc19de8a4df39ad421cce6205001df12 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jul 2013 01:45:43 +0200 Subject: [PATCH 246/638] Add support for mirrors in composer repos --- src/Composer/Downloader/FileDownloader.php | 28 ++++++-- src/Composer/Downloader/GitDownloader.php | 14 ++-- src/Composer/Downloader/HgDownloader.php | 8 +-- src/Composer/Downloader/SvnDownloader.php | 6 +- src/Composer/Downloader/VcsDownloader.php | 49 ++++++++++--- src/Composer/Package/AliasPackage.php | 24 +++++++ src/Composer/Package/Dumper/ArrayDumper.php | 6 ++ src/Composer/Package/Loader/ArrayLoader.php | 6 ++ src/Composer/Package/Package.php | 68 +++++++++++++++++++ src/Composer/Package/PackageInterface.php | 28 ++++++++ .../Repository/ComposerRepository.php | 15 ++++ .../Test/Downloader/FileDownloaderTest.php | 16 ++++- .../Test/Downloader/GitDownloaderTest.php | 24 +++---- .../Test/Downloader/HgDownloaderTest.php | 8 +-- .../Test/Downloader/ZipDownloaderTest.php | 6 +- 15 files changed, 258 insertions(+), 48 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 8c98eb785..07a768985 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -79,18 +79,38 @@ class FileDownloader implements DownloaderInterface */ public function download(PackageInterface $package, $path) { - $url = $package->getDistUrl(); - if (!$url) { + if (!$package->getDistUrl()) { throw new \InvalidArgumentException('The given package is missing url information'); } + $this->io->write(" - Installing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")"); + + $urls = $package->getDistUrls(); + while ($url = array_shift($urls)) { + try { + $this->doDownload($package, $path, $url); + break; + } catch (\Exception $e) { + if ($this->io->isDebug()) { + $this->io->write(''); + $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage()); + } elseif (count($urls)) { + $this->io->write(''); + $this->io->write(' Failed, trying the next URL'); + } else { + throw $e; + } + } + } + } + + protected function doDownload(PackageInterface $package, $path, $url) + { $this->filesystem->removeDirectory($path); $this->filesystem->ensureDirectoryExists($path); $fileName = $this->getFileName($package, $path); - $this->io->write(" - Installing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")"); - $processedUrl = $this->processUrl($package, $url); $hostname = parse_url($processedUrl, PHP_URL_HOST); diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 3186117ae..2dc7532a9 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -26,7 +26,7 @@ class GitDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path) + public function doDownload(PackageInterface $package, $path, $url) { $this->cleanEnv(); $path = $this->normalizePath($path); @@ -40,8 +40,8 @@ class GitDownloader extends VcsDownloader return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref)); }; - $this->runCommand($commandCallable, $package->getSourceUrl(), $path, true); - $this->setPushUrl($package, $path); + $this->runCommand($commandCallable, $url, $path, true); + $this->setPushUrl($path, $url); if ($newRef = $this->updateToCommit($path, $ref, $package->getPrettyVersion(), $package->getReleaseDate())) { if ($package->getDistReference() === $package->getSourceReference()) { @@ -54,7 +54,7 @@ class GitDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) + public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { $this->cleanEnv(); $path = $this->normalizePath($path); @@ -76,7 +76,7 @@ class GitDownloader extends VcsDownloader return sprintf($command, escapeshellarg($url)); }; - $this->runCommand($commandCallable, $target->getSourceUrl(), $path); + $this->runCommand($commandCallable, $url, $path); if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) { if ($target->getDistReference() === $target->getSourceReference()) { $target->setDistReference($newRef); @@ -412,10 +412,10 @@ class GitDownloader extends VcsDownloader return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message); } - protected function setPushUrl(PackageInterface $package, $path) + protected function setPushUrl($path, $url) { // set push url for github projects - if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $package->getSourceUrl(), $match)) { + if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $url, $match)) { $protocols = $this->config->get('github-protocols'); $pushUrl = 'git@'.$match[1].':'.$match[2].'/'.$match[3].'.git'; if ($protocols[0] !== 'git') { diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 7252bf4fe..69e8ba886 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -22,9 +22,9 @@ class HgDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path) + public function doDownload(PackageInterface $package, $path, $url) { - $url = escapeshellarg($package->getSourceUrl()); + $url = escapeshellarg($url); $ref = escapeshellarg($package->getSourceReference()); $this->io->write(" Cloning ".$package->getSourceReference()); $command = sprintf('hg clone %s %s', $url, escapeshellarg($path)); @@ -40,9 +40,9 @@ class HgDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) + public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $url = escapeshellarg($target->getSourceUrl()); + $url = escapeshellarg($url); $ref = escapeshellarg($target->getSourceReference()); $this->io->write(" Updating to ".$target->getSourceReference()); diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 8fefbdfc1..495979d40 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -24,10 +24,9 @@ class SvnDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path) + public function doDownload(PackageInterface $package, $path, $url) { SvnUtil::cleanEnv(); - $url = $package->getSourceUrl(); $ref = $package->getSourceReference(); $this->io->write(" Checking out ".$package->getSourceReference()); @@ -37,10 +36,9 @@ class SvnDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) + public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { SvnUtil::cleanEnv(); - $url = $target->getSourceUrl(); $ref = $target->getSourceReference(); if (!is_dir($path.'/.svn')) { diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 4e06d63f8..7ebadf51c 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -56,7 +56,23 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->io->write(" - Installing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")"); $this->filesystem->removeDirectory($path); - $this->doDownload($package, $path); + + $urls = $package->getSourceUrls(); + while ($url = array_shift($urls)) { + try { + $this->doDownload($package, $path, $url); + break; + } catch (\Exception $e) { + if ($this->io->isDebug()) { + $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage()); + } elseif (count($urls)) { + $this->io->write(' Failed, trying the next URL'); + } else { + throw $e; + } + } + } + $this->io->write(''); } @@ -87,17 +103,28 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->io->write(" - Updating " . $name . " (" . $from . " => " . $to . ")"); $this->cleanChanges($initial, $path, true); - try { - $this->doUpdate($initial, $target, $path); - } catch (\Exception $e) { - // in case of failed update, try to reapply the changes before aborting - $this->reapplyChanges($path); + $urls = $target->getSourceUrls(); + while ($url = array_shift($urls)) { + try { + $this->doUpdate($initial, $target, $path, $url); + break; + } catch (\Exception $e) { + if ($this->io->isDebug()) { + $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage()); + } elseif (count($urls)) { + $this->io->write(' Failed, trying the next URL'); + } else { + // in case of failed update, try to reapply the changes before aborting + $this->reapplyChanges($path); - throw $e; + throw $e; + } + } } + $this->reapplyChanges($path); - //print the commit logs if in verbose mode + // print the commit logs if in verbose mode if ($this->io->isVerbose()) { $message = 'Pulling in changes:'; $logs = $this->getCommitLogs($initial->getSourceReference(), $target->getSourceReference(), $path); @@ -176,8 +203,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa * * @param PackageInterface $package package instance * @param string $path download path + * @param string $url package url */ - abstract protected function doDownload(PackageInterface $package, $path); + abstract protected function doDownload(PackageInterface $package, $path, $url); /** * Updates specific package in specific folder from initial to target version. @@ -185,8 +213,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa * @param PackageInterface $initial initial package * @param PackageInterface $target updated package * @param string $path download path + * @param string $url package url */ - abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path); + abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url); /** * Fetches the commit logs between two commits diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index c90221027..cf4da14ba 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -209,6 +209,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getSourceUrl(); } + public function getSourceUrls() + { + return $this->aliasOf->getSourceUrls(); + } public function getSourceReference() { return $this->aliasOf->getSourceReference(); @@ -217,6 +221,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->setSourceReference($reference); } + public function setSourceMirrors($mirrors) + { + return $this->aliasOf->setSourceMirrors($mirrors); + } + public function getSourceMirrors() + { + return $this->aliasOf->getSourceMirrors(); + } public function getDistType() { return $this->aliasOf->getDistType(); @@ -225,6 +237,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getDistUrl(); } + public function getDistUrls() + { + return $this->aliasOf->getDistUrls(); + } public function getDistReference() { return $this->aliasOf->getDistReference(); @@ -241,6 +257,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getTransportOptions(); } + public function setDistMirrors($mirrors) + { + return $this->aliasOf->setDistMirrors($mirrors); + } + public function getDistMirrors() + { + return $this->aliasOf->getDistMirrors(); + } public function getScripts() { return $this->aliasOf->getScripts(); diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 8741acefb..67318c04a 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -49,6 +49,9 @@ class ArrayDumper $data['source']['type'] = $package->getSourceType(); $data['source']['url'] = $package->getSourceUrl(); $data['source']['reference'] = $package->getSourceReference(); + if ($mirrors = $package->getSourceMirrors()) { + $data['source']['mirrors'] = $mirrors; + } } if ($package->getDistType()) { @@ -56,6 +59,9 @@ class ArrayDumper $data['dist']['url'] = $package->getDistUrl(); $data['dist']['reference'] = $package->getDistReference(); $data['dist']['shasum'] = $package->getDistSha1Checksum(); + if ($mirrors = $package->getDistMirrors()) { + $data['dist']['mirrors'] = $mirrors; + } } if ($package->getArchiveExcludes()) { diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 66d48f65e..142b15c72 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -87,6 +87,9 @@ class ArrayLoader implements LoaderInterface $package->setSourceType($config['source']['type']); $package->setSourceUrl($config['source']['url']); $package->setSourceReference($config['source']['reference']); + if (isset($config['source']['mirrors'])) { + $package->setSourceMirrors($config['source']['mirrors']); + } } if (isset($config['dist'])) { @@ -103,6 +106,9 @@ class ArrayLoader implements LoaderInterface $package->setDistUrl($config['dist']['url']); $package->setDistReference(isset($config['dist']['reference']) ? $config['dist']['reference'] : null); $package->setDistSha1Checksum(isset($config['dist']['shasum']) ? $config['dist']['shasum'] : null); + if (isset($config['dist']['mirrors'])) { + $package->setDistMirrors($config['dist']['mirrors']); + } } foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) { diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 8fab59a8a..ffc759f62 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -27,10 +27,12 @@ class Package extends BasePackage protected $sourceType; protected $sourceUrl; protected $sourceReference; + protected $sourceMirrors; protected $distType; protected $distUrl; protected $distReference; protected $distSha1Checksum; + protected $distMirrors; protected $version; protected $prettyVersion; protected $releaseDate; @@ -217,6 +219,30 @@ class Package extends BasePackage return $this->sourceReference; } + /** + * @param array|null $mirrors + */ + public function setSourceMirrors($mirrors) + { + $this->sourceMirrors = $mirrors; + } + + /** + * {@inheritDoc} + */ + public function getSourceMirrors() + { + return $this->sourceMirrors; + } + + /** + * {@inheritDoc} + */ + public function getSourceUrls() + { + return $this->getUrls($this->sourceUrl, $this->sourceMirrors, $this->sourceReference, $this->sourceType); + } + /** * @param string $type */ @@ -281,6 +307,30 @@ class Package extends BasePackage return $this->distSha1Checksum; } + /** + * @param array|null $mirrors + */ + public function setDistMirrors($mirrors) + { + $this->distMirrors = $mirrors; + } + + /** + * {@inheritDoc} + */ + public function getDistMirrors() + { + return $this->distMirrors; + } + + /** + * {@inheritDoc} + */ + public function getDistUrls() + { + return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType); + } + /** * {@inheritDoc} */ @@ -528,4 +578,22 @@ class Package extends BasePackage $this->stability = VersionParser::parseStability($version); $this->dev = $this->stability === 'dev'; } + + protected function getUrls($url, $mirrors, $ref, $type) + { + $urls = array($url); + if ($mirrors) { + foreach ($mirrors as $mirror) { + $mirrorUrl = str_replace( + array('%package%', '%version%', '%reference%', '%type%'), + array($this->name, $this->version, $ref, $type), + $mirror['url'] + ); + $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; + $func($urls, $mirrorUrl); + } + } + + return $urls; + } } diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 337a72a83..50553f9c4 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -115,6 +115,13 @@ interface PackageInterface */ public function getSourceUrl(); + /** + * Returns the repository urls of this package including mirrors, e.g. git://github.com/naderman/composer.git + * + * @return string + */ + public function getSourceUrls(); + /** * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git * @@ -122,6 +129,13 @@ interface PackageInterface */ public function getSourceReference(); + /** + * Returns the source mirrors of this package + * + * @return array|null + */ + public function getSourceMirrors(); + /** * Returns the type of the distribution archive of this version, e.g. zip, tarball * @@ -136,6 +150,13 @@ interface PackageInterface */ public function getDistUrl(); + /** + * Returns the urls of the distribution archive of this version, including mirrors + * + * @return string + */ + public function getDistUrls(); + /** * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git * @@ -150,6 +171,13 @@ interface PackageInterface */ public function getDistSha1Checksum(); + /** + * Returns the dist mirrors of this package + * + * @return array|null + */ + public function getDistMirrors(); + /** * Returns the version of this package * diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 8ed6897c4..ba6087027 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -50,6 +50,8 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository protected $rootAliases; protected $allowSslDowngrade = false; protected $eventDispatcher; + protected $sourceMirrors; + protected $distMirrors; private $rawData; private $minimalPackages; private $degradedMode = false; @@ -434,6 +436,17 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $this->searchUrl = $this->canonicalizeUrl($data['search']); } + if (!empty($data['mirrors'])) { + foreach ($data['mirrors'] as $mirror) { + if (!empty($mirror['source-url'])) { + $this->sourceMirrors[] = array('url' => $mirror['source-url'], 'preferred' => !empty($mirror['preferred'])); + } + if (!empty($mirror['dist-url'])) { + $this->distMirrors[] = array('url' => $mirror['dist-url'], 'preferred' => !empty($mirror['preferred'])); + } + } + } + if ($this->allowSslDowngrade) { $this->url = str_replace('https://', 'http://', $this->url); } @@ -548,6 +561,8 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } $package = $this->loader->load($data, 'Composer\Package\CompletePackage'); + $package->setSourceMirrors($this->sourceMirrors); + $package->setDistMirrors($this->distMirrors); $this->configurePackageTransportOptions($package); return $package; diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php index 28714de4a..ffdbf997d 100644 --- a/tests/Composer/Test/Downloader/FileDownloaderTest.php +++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php @@ -48,6 +48,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getDistUrl') ->will($this->returnValue('url')) ; + $packageMock->expects($this->once()) + ->method('getDistUrls') + ->will($this->returnValue(array('url'))) + ; $path = tempnam(sys_get_temp_dir(), 'c'); @@ -87,7 +91,11 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getDistUrl') - ->will($this->returnValue('http://example.com/script.js')) + ->will($this->returnValue($distUrl = 'http://example.com/script.js')) + ; + $packageMock->expects($this->once()) + ->method('getDistUrls') + ->will($this->returnValue(array($distUrl))) ; $packageMock->expects($this->atLeastOnce()) ->method('getTransportOptions') @@ -163,7 +171,7 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getDistUrl') - ->will($this->returnValue('http://example.com/script.js')) + ->will($this->returnValue($distUrl = 'http://example.com/script.js')) ; $packageMock->expects($this->atLeastOnce()) ->method('getTransportOptions') @@ -173,6 +181,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getDistSha1Checksum') ->will($this->returnValue('invalid')) ; + $packageMock->expects($this->once()) + ->method('getDistUrls') + ->will($this->returnValue(array($distUrl))) + ; $filesystem = $this->getMock('Composer\Util\Filesystem'); do { diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 01142580e..194fc96c9 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -51,8 +51,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('1234567890123456789012345678901234567890')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://example.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://example.com/composer/composer'))); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('dev-master')); @@ -90,8 +90,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://github.com/composer/composer'))); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('1.0.0')); @@ -147,8 +147,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://github.com/composer/composer'))); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('1.0.0')); @@ -188,8 +188,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://example.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://example.com/composer/composer'))); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $processExecutor->expects($this->at(0)) ->method('execute') @@ -227,8 +227,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://github.com/composer/composer'))); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('1.0.0')); @@ -273,8 +273,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/composer/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://github.com/composer/composer'))); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $processExecutor->expects($this->at(0)) ->method('execute') diff --git a/tests/Composer/Test/Downloader/HgDownloaderTest.php b/tests/Composer/Test/Downloader/HgDownloaderTest.php index 37a895172..ab9ec28cd 100644 --- a/tests/Composer/Test/Downloader/HgDownloaderTest.php +++ b/tests/Composer/Test/Downloader/HgDownloaderTest.php @@ -48,8 +48,8 @@ class HgDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->once()) - ->method('getSourceUrl') - ->will($this->returnValue('https://mercurial.dev/l3l0/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://mercurial.dev/l3l0/composer'))); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $expectedGitCommand = $this->getCmd('hg clone \'https://mercurial.dev/l3l0/composer\' \'composerPath\''); @@ -93,8 +93,8 @@ class HgDownloaderTest extends \PHPUnit_Framework_TestCase ->method('getSourceReference') ->will($this->returnValue('ref')); $packageMock->expects($this->any()) - ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/l3l0/composer')); + ->method('getSourceUrls') + ->will($this->returnValue(array('https://github.com/l3l0/composer'))); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $expectedHgCommand = $this->getCmd("hg st"); diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php index 36b2c2296..58e0078b0 100644 --- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php @@ -28,7 +28,11 @@ class ZipDownloaderTest extends \PHPUnit_Framework_TestCase $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getDistUrl') - ->will($this->returnValue('file://'.__FILE__)) + ->will($this->returnValue($distUrl = 'file://'.__FILE__)) + ; + $packageMock->expects($this->any()) + ->method('getDistUrls') + ->will($this->returnValue(array($distUrl))) ; $packageMock->expects($this->atLeastOnce()) ->method('getTransportOptions') From 44e45ed2d5ad3a5716645d7539d01ee112bcc1b1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jul 2013 01:46:20 +0200 Subject: [PATCH 247/638] Add support for lazy providers/proxies --- .../Repository/ComposerRepository.php | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index ba6087027..d1b0f6604 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -43,6 +43,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository protected $searchUrl; protected $hasProviders = false; protected $providersUrl; + protected $lazyProvidersUrl; protected $providerListing; protected $providers = array(); protected $providersByUid = array(); @@ -267,7 +268,11 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $this->loadProviderListings($this->loadRootServerFile()); } - if ($this->providersUrl) { + if ($this->lazyProvidersUrl && !isset($this->providerListing[$name])) { + $hash = $this->providerListing[$name]['sha256']; + $url = str_replace('%package%', $name, $this->lazyProvidersUrl); + $cacheKey = false; + } elseif ($this->providersUrl) { // package does not exist in this repo if (!isset($this->providerListing[$name])) { return array(); @@ -288,7 +293,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $cacheKey = null; } - if ($this->cache->sha256($cacheKey) === $hash) { + if ($cacheKey && $this->cache->sha256($cacheKey) === $hash) { $packages = json_decode($this->cache->read($cacheKey), true); } else { $packages = $this->fetchFile($url, $cacheKey, $hash); @@ -447,6 +452,11 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } } + if (!empty($data['providers-lazy-url'])) { + $this->lazyProvidersUrl = $this->canonicalizeUrl($data['providers-lazy-url']); + $this->hasProviders = true; + } + if ($this->allowSslDowngrade) { $this->url = str_replace('https://', 'http://', $this->url); } @@ -573,7 +583,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository protected function fetchFile($filename, $cacheKey = null, $sha256 = null) { - if (!$cacheKey) { + if (null === $cacheKey) { $cacheKey = $filename; $filename = $this->baseUrl.'/'.$filename; } @@ -597,7 +607,9 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository throw new RepositorySecurityException('The contents of '.$filename.' do not match its signature. This should indicate a man-in-the-middle attack. Try running composer again and report this if you think it is a mistake.'); } $data = JsonFile::parseJson($json, $filename); - $this->cache->write($cacheKey, $json); + if ($cacheKey) { + $this->cache->write($cacheKey, $json); + } break; } catch (\Exception $e) { @@ -610,7 +622,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository throw $e; } - if ($contents = $this->cache->read($cacheKey)) { + if ($cacheKey && ($contents = $this->cache->read($cacheKey))) { if (!$this->degradedMode) { $this->io->write(''.$e->getMessage().''); $this->io->write(''.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date'); From 9cbfe319834bb8b1e481ca00fe012c4bcf01da64 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jul 2013 01:05:54 +0200 Subject: [PATCH 248/638] Fix regression when using -vvv --- src/Composer/Downloader/FileDownloader.php | 4 +++- src/Composer/Downloader/VcsDownloader.php | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 07a768985..28dd2d779 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -97,7 +97,9 @@ class FileDownloader implements DownloaderInterface } elseif (count($urls)) { $this->io->write(''); $this->io->write(' Failed, trying the next URL'); - } else { + } + + if (!count($urls)) { throw $e; } } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 7ebadf51c..bff9201f7 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -67,7 +67,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage()); } elseif (count($urls)) { $this->io->write(' Failed, trying the next URL'); - } else { + } + if (!count($urls)) { throw $e; } } From ba776c06ee13f947dd7b594ee387e869564e5ee7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 17 Oct 2013 20:29:25 +0200 Subject: [PATCH 249/638] Add composer mirror class --- src/Composer/Downloader/FileDownloader.php | 3 +- src/Composer/Package/Package.php | 12 ++++---- src/Composer/Util/ComposerMirror.php | 33 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 src/Composer/Util/ComposerMirror.php diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 28dd2d779..ddde199b6 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -88,8 +88,7 @@ class FileDownloader implements DownloaderInterface $urls = $package->getDistUrls(); while ($url = array_shift($urls)) { try { - $this->doDownload($package, $path, $url); - break; + return $this->doDownload($package, $path, $url); } catch (\Exception $e) { if ($this->io->isDebug()) { $this->io->write(''); diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index ffc759f62..970068cd2 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -13,6 +13,7 @@ namespace Composer\Package; use Composer\Package\Version\VersionParser; +use Composer\Util\ComposerMirror; /** * Core package definitions that are needed to resolve dependencies and install packages @@ -581,14 +582,13 @@ class Package extends BasePackage protected function getUrls($url, $mirrors, $ref, $type) { - $urls = array($url); + $urls = array(); + if ($url) { + $urls[] = $url; + } if ($mirrors) { foreach ($mirrors as $mirror) { - $mirrorUrl = str_replace( - array('%package%', '%version%', '%reference%', '%type%'), - array($this->name, $this->version, $ref, $type), - $mirror['url'] - ); + $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type); $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; $func($urls, $mirrorUrl); } diff --git a/src/Composer/Util/ComposerMirror.php b/src/Composer/Util/ComposerMirror.php new file mode 100644 index 000000000..710f52cce --- /dev/null +++ b/src/Composer/Util/ComposerMirror.php @@ -0,0 +1,33 @@ + Date: Mon, 19 Aug 2013 01:20:34 +0200 Subject: [PATCH 250/638] Minor fixes --- src/Composer/Package/Package.php | 12 +++++++----- src/Composer/Util/ComposerMirror.php | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 970068cd2..6dbd78911 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -582,15 +582,17 @@ class Package extends BasePackage protected function getUrls($url, $mirrors, $ref, $type) { - $urls = array(); - if ($url) { - $urls[] = $url; + if (!$url) { + return array(); } + $urls = array($url); if ($mirrors) { foreach ($mirrors as $mirror) { $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type); - $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; - $func($urls, $mirrorUrl); + if (!in_array($urls, $mirrorUrl)) { + $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; + $func($urls, $mirrorUrl); + } } } diff --git a/src/Composer/Util/ComposerMirror.php b/src/Composer/Util/ComposerMirror.php index 710f52cce..f5c2000db 100644 --- a/src/Composer/Util/ComposerMirror.php +++ b/src/Composer/Util/ComposerMirror.php @@ -1,22 +1,22 @@ + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace Composer\Util; - - - - - +/** + * Composer mirror utilities + * + * @author Jordi Boggiano + */ class ComposerMirror { public static function processUrl($mirrorUrl, $packageName, $version, $reference, $type) From 31b787249c22357b6b14306eb9766af5b1ac7754 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Oct 2013 17:41:29 +0200 Subject: [PATCH 251/638] More fixes to mirror support --- src/Composer/Package/Package.php | 16 +++++++++----- .../Repository/ComposerRepository.php | 15 ++++++++++--- src/Composer/Util/ComposerMirror.php | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 6dbd78911..2662fd32a 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -241,7 +241,7 @@ class Package extends BasePackage */ public function getSourceUrls() { - return $this->getUrls($this->sourceUrl, $this->sourceMirrors, $this->sourceReference, $this->sourceType); + return $this->getUrls($this->sourceUrl, $this->sourceMirrors, $this->sourceReference, $this->sourceType, 'source'); } /** @@ -329,7 +329,7 @@ class Package extends BasePackage */ public function getDistUrls() { - return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType); + return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType, 'dist'); } /** @@ -580,7 +580,7 @@ class Package extends BasePackage $this->dev = $this->stability === 'dev'; } - protected function getUrls($url, $mirrors, $ref, $type) + protected function getUrls($url, $mirrors, $ref, $type, $urlType) { if (!$url) { return array(); @@ -588,8 +588,14 @@ class Package extends BasePackage $urls = array($url); if ($mirrors) { foreach ($mirrors as $mirror) { - $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type); - if (!in_array($urls, $mirrorUrl)) { + if ($urlType === 'dist') { + $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type); + } elseif ($urlType === 'source' && $type === 'git') { + $mirrorUrl = ComposerMirror::processGitUrl($mirror['url'], $this->name, $url, $type); + } elseif ($urlType === 'source' && $type === 'hg') { + $mirrorUrl = ComposerMirror::processHgUrl($mirror['url'], $this->name, $url, $type); + } + if (!in_array($mirrorUrl, $urls)) { $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; $func($urls, $mirrorUrl); } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index d1b0f6604..5f5bf445b 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -443,8 +443,11 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository if (!empty($data['mirrors'])) { foreach ($data['mirrors'] as $mirror) { - if (!empty($mirror['source-url'])) { - $this->sourceMirrors[] = array('url' => $mirror['source-url'], 'preferred' => !empty($mirror['preferred'])); + if (!empty($mirror['git-url'])) { + $this->sourceMirrors['git'][] = array('url' => $mirror['git-url'], 'preferred' => !empty($mirror['preferred'])); + } + if (!empty($mirror['hg-url'])) { + $this->sourceMirrors['hg'][] = array('url' => $mirror['hg-url'], 'preferred' => !empty($mirror['preferred'])); } if (!empty($mirror['dist-url'])) { $this->distMirrors[] = array('url' => $mirror['dist-url'], 'preferred' => !empty($mirror['preferred'])); @@ -452,6 +455,10 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } } + if (!empty($data['warning'])) { + $this->io->write('Warning from '.$this->url.': '.$data['warning'].''); + } + if (!empty($data['providers-lazy-url'])) { $this->lazyProvidersUrl = $this->canonicalizeUrl($data['providers-lazy-url']); $this->hasProviders = true; @@ -571,7 +578,9 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } $package = $this->loader->load($data, 'Composer\Package\CompletePackage'); - $package->setSourceMirrors($this->sourceMirrors); + if (isset($this->sourceMirrors[$package->getSourceType()])) { + $package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]); + } $package->setDistMirrors($this->distMirrors); $this->configurePackageTransportOptions($package); diff --git a/src/Composer/Util/ComposerMirror.php b/src/Composer/Util/ComposerMirror.php index f5c2000db..ce27af21a 100644 --- a/src/Composer/Util/ComposerMirror.php +++ b/src/Composer/Util/ComposerMirror.php @@ -30,4 +30,26 @@ class ComposerMirror $mirrorUrl ); } + + public static function processGitUrl($mirrorUrl, $packageName, $url, $type) + { + if (preg_match('#^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$#', $url, $match)) { + $url = 'gh-'.$match[1].'/'.$match[2]; + } elseif (preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$#', $url, $match)) { + $url = 'bb-'.$match[1].'/'.$match[2]; + } else { + $url = preg_replace('{[^a-z0-9_.-]}i', '-', trim($url, '/')); + } + + return str_replace( + array('%package%', '%normalizedUrl%', '%type%'), + array($packageName, $url, $type), + $mirrorUrl + ); + } + + public static function processHgUrl($mirrorUrl, $packageName, $url, $type) + { + return self::processGitUrl($mirrorUrl, $packageName, $url, $type); + } } From 97873a27af2c382a93ebc909662d1a7150d51ec7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Oct 2013 17:55:30 +0200 Subject: [PATCH 252/638] Update perforce code --- src/Composer/Downloader/PerforceDownloader.php | 12 ++++++------ .../Test/Downloader/PerforceDownloaderTest.php | 9 ++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 8892e4b74..42f424057 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -26,13 +26,13 @@ class PerforceDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path) + public function doDownload(PackageInterface $package, $path, $url) { $ref = $package->getSourceReference(); $label = $this->getLabelFromSourceReference($ref); $this->io->write(' Cloning ' . $ref); - $this->initPerforce($package, $path); + $this->initPerforce($package, $path, $url); $this->perforce->setStream($ref); $this->perforce->p4Login($this->io); $this->perforce->writeP4ClientSpec(); @@ -51,7 +51,7 @@ class PerforceDownloader extends VcsDownloader return null; } - public function initPerforce($package, $path) + public function initPerforce($package, $path, $url) { if (!empty($this->perforce)) { $this->perforce->initializePath($path); @@ -63,7 +63,7 @@ class PerforceDownloader extends VcsDownloader if ($repository instanceof VcsRepository) { $repoConfig = $this->getRepoConfig($repository); } - $this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path, $this->process, $this->io); + $this->perforce = Perforce::create($repoConfig, $url, $path, $this->process, $this->io); } private function getRepoConfig(VcsRepository $repository) @@ -74,9 +74,9 @@ class PerforceDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) + public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $this->doDownload($target, $path); + $this->doDownload($target, $path, $url); } /** diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 7562f7fca..b1d3d8fb9 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -22,7 +22,7 @@ use Composer\IO\IOInterface; */ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase { - + protected $config; protected $downloader; protected $io; @@ -82,7 +82,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase protected function getRepoConfig() { - return array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); + return array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); } protected function getMockRepository(array $repoConfig, IOInterface $io, Config $config) @@ -129,7 +129,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $perforce->expects($this->at(5))->method('syncCodeBase')->with($label); $perforce->expects($this->at(6))->method('cleanupClientSpec'); $this->downloader->setPerforce($perforce); - $this->downloader->doDownload($this->package, $this->testPath); + $this->downloader->doDownload($this->package, $this->testPath, 'url'); } /** @@ -152,7 +152,6 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $perforce->expects($this->at(5))->method('syncCodeBase')->with($label); $perforce->expects($this->at(6))->method('cleanupClientSpec'); $this->downloader->setPerforce($perforce); - $this->downloader->doDownload($this->package, $this->testPath); + $this->downloader->doDownload($this->package, $this->testPath, 'url'); } - } From 39c8d3e6e1f495b29060ad3a791c6b076ce776ea Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 11 Apr 2014 13:58:47 +0200 Subject: [PATCH 253/638] Ignore empty refs --- src/Composer/Util/ComposerMirror.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/ComposerMirror.php b/src/Composer/Util/ComposerMirror.php index ce27af21a..036444d20 100644 --- a/src/Composer/Util/ComposerMirror.php +++ b/src/Composer/Util/ComposerMirror.php @@ -21,7 +21,9 @@ class ComposerMirror { public static function processUrl($mirrorUrl, $packageName, $version, $reference, $type) { - $reference = preg_match('{^([a-f0-9]*|%reference%)$}', $reference) ? $reference : md5($reference); + if ($reference) { + $reference = preg_match('{^([a-f0-9]*|%reference%)$}', $reference) ? $reference : md5($reference); + } $version = strpos($version, '/') === false ? $version : md5($version); return str_replace( From b6981d09e8e37fcbe7e589a4c35474a21f3776a3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 18:38:58 +0200 Subject: [PATCH 254/638] Fix handling of origin url in composer repository class --- src/Composer/Command/DiagnoseCommand.php | 4 ++-- src/Composer/Downloader/FileDownloader.php | 4 ---- src/Composer/Repository/ComposerRepository.php | 7 +++++-- src/Composer/Util/RemoteFilesystem.php | 4 ++++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 21523a062..2661837a3 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -207,10 +207,10 @@ EOT $url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0'; try { - $rfcResult = $this->rfs->getContents('api.github.com', $url, false); + $rfcResult = $this->rfs->getContents('github.com', $url, false); } catch (TransportException $e) { try { - $this->rfs->getContents('api.github.com', $url, false, array('http' => array('request_fulluri' => false))); + $this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false))); } catch (TransportException $e) { return 'Unable to assert the situation, maybe github is down ('.$e->getMessage().')'; } diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index ddde199b6..709835444 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -121,10 +121,6 @@ class FileDownloader implements DownloaderInterface } $rfs = $preFileDownloadEvent->getRemoteFilesystem(); - if (strpos($hostname, '.github.com') === (strlen($hostname) - 11)) { - $hostname = 'github.com'; - } - try { $checksum = $package->getDistSha1Checksum(); $cacheKey = $this->getCacheKey($package); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5f5bf445b..6245cd031 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -160,7 +160,8 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository if ($this->searchUrl && $mode === self::SEARCH_FULLTEXT) { $url = str_replace('%query%', $query, $this->searchUrl); - $json = $this->rfs->getContents($url, $url, false); + $hostname = parse_url($url, PHP_URL_HOST) ?: $url; + $json = $this->rfs->getContents($hostname, $url, false); $results = JsonFile::parseJson($json, $url); return $results['results']; @@ -604,7 +605,9 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository if ($this->eventDispatcher) { $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); } - $json = $preFileDownloadEvent->getRemoteFilesystem()->getContents($filename, $filename, false); + + $hostname = parse_url($filename, PHP_URL_HOST) ?: $filename; + $json = $preFileDownloadEvent->getRemoteFilesystem()->getContents($hostname, $filename, false); if ($sha256 && $sha256 !== hash('sha256', $json)) { if ($retries) { usleep(100000); diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 2f9dacf69..bac6e1df0 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -118,6 +118,10 @@ class RemoteFilesystem */ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true) { + if (strpos($originUrl, '.github.com') === (strlen($originUrl) - 11)) { + $originUrl = 'github.com'; + } + $this->bytesMax = 0; $this->originUrl = $originUrl; $this->fileUrl = $fileUrl; From c811eded7db100c4d03d5faaebb3bdf5219eca5c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 18:45:34 +0200 Subject: [PATCH 255/638] Do not output if we are about to retry a download --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index bac6e1df0..108c22f85 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -222,7 +222,7 @@ class RemoteFilesystem } } - if ($this->progress) { + if ($this->progress && !$this->retry) { $this->io->overwrite(" Downloading: 100%"); } From 2a7a954f62186d09273f635f6115bf280284aeeb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 7 May 2014 19:12:58 +0200 Subject: [PATCH 256/638] Handle multiple urls in package transport options --- src/Composer/Repository/ComposerRepository.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 6245cd031..1ec62b3d2 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -220,9 +220,11 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository protected function configurePackageTransportOptions(PackageInterface $package) { - if (strpos($package->getDistUrl(), $this->baseUrl) === 0) { - $package->setTransportOptions($this->options); - return; + foreach ($package->getDistUrls() as $url) { + if (strpos($url, $this->baseUrl) === 0) { + $package->setTransportOptions($this->options); + return; + } } } From 5f86037ff307ff985e62e380b6ef1f07ece29d15 Mon Sep 17 00:00:00 2001 From: Tristan Lins Date: Mon, 12 May 2014 23:12:57 +0200 Subject: [PATCH 257/638] Graceful fallback in Filesystem::removeDirectory() to php, if process failed. --- src/Composer/Util/Filesystem.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 8771776fc..d88e00393 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -85,10 +85,14 @@ class Filesystem $result = $this->getProcess()->execute($cmd, $output) === 0; - // clear stat cache because external processes aren't tracked by the php stat cache - clearstatcache(); + if ($result) { + // clear stat cache because external processes aren't tracked by the php stat cache + clearstatcache(); - return $result && !is_dir($directory); + return !is_dir($directory); + } + + return $this->removeDirectoryPhp($directory); } /** From a4d43ee8605e8c48d8a8b277720dea9f92119d82 Mon Sep 17 00:00:00 2001 From: Tristan Lins Date: Tue, 13 May 2014 23:54:48 +0200 Subject: [PATCH 258/638] Implement ComposerRepository::findPackage and ComposerRepository::findPackages. --- .../Repository/ComposerRepository.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 1d686cae1..82e886d32 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -94,6 +94,58 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository $this->rootAliases = $rootAliases; } + /** + * {@inheritDoc} + */ + public function findPackage($name, $version) + { + // normalize version & name + $versionParser = new VersionParser(); + $version = $versionParser->normalize($version); + $name = strtolower($name); + + foreach ($this->getProviderNames() as $providerName) { + if ($name === $providerName) { + $packages = $this->whatProvides(new Pool('dev'), $providerName); + foreach ($packages as $package) { + if ($name == $package->getName() && $version === $package->getVersion()) { + return $package; + } + } + } + } + } + + /** + * {@inheritDoc} + */ + public function findPackages($name, $version = null) + { + // normalize name + $name = strtolower($name); + + // normalize version + if (null !== $version) { + $versionParser = new VersionParser(); + $version = $versionParser->normalize($version); + } + + $packages = array(); + + foreach ($this->getProviderNames() as $providerName) { + if ($name === $providerName) { + $packages = $this->whatProvides(new Pool('dev'), $providerName); + foreach ($packages as $package) { + if ($name == $package->getName() && (null === $version || $version === $package->getVersion())) { + $packages[] = $package; + } + } + } + } + + return $packages; + } + public function getPackages() { if ($this->hasProviders()) { From 0c343f925ad578a9da4d9a4cfff4e91b500d206c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 14 May 2014 11:25:20 +0200 Subject: [PATCH 259/638] Clarify code --- src/Composer/Repository/ComposerRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 1ec62b3d2..0b2ef0106 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -272,7 +272,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } if ($this->lazyProvidersUrl && !isset($this->providerListing[$name])) { - $hash = $this->providerListing[$name]['sha256']; + $hash = null; $url = str_replace('%package%', $name, $this->lazyProvidersUrl); $cacheKey = false; } elseif ($this->providersUrl) { From 851082e9f48097211a5c683d223301ca6c9361ca Mon Sep 17 00:00:00 2001 From: Dawid Nowak Date: Fri, 16 May 2014 03:48:30 +0200 Subject: [PATCH 260/638] JsonConfigSource->array_unshift_ref() set private and changed name to arrayUnshiftRef() --- src/Composer/Config/JsonConfigSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 1c12aadcf..6a92131bd 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -120,7 +120,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { // on failed clean update, call the fallback and rewrite the whole file $config = $this->file->read(); - $this->array_unshift_ref($args, $config); + $this->arrayUnshiftRef($args, $config); call_user_func_array($fallback, $args); $this->file->write($config); } @@ -137,7 +137,7 @@ class JsonConfigSource implements ConfigSourceInterface * @param mixed $value * @return array */ - function array_unshift_ref(&$array, &$value) + private function arrayUnshiftRef(&$array, &$value) { $return = array_unshift($array, ''); $array[0] =& $value; From 0c85ca426d6e8235366340f29ad0e6aab1747b83 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 19 May 2014 12:17:07 +0200 Subject: [PATCH 261/638] Add code-fences to examples for syntax highlighting --- doc/00-intro.md | 60 ++- doc/01-basic-usage.md | 62 ++- doc/02-libraries.md | 72 +-- doc/03-cli.md | 149 ++++-- doc/04-schema.md | 427 ++++++++++-------- doc/05-repositories.md | 336 +++++++------- doc/articles/aliases.md | 34 +- doc/articles/custom-installers.md | 122 ++--- .../handling-private-packages-with-satis.md | 139 +++--- doc/articles/plugins.md | 106 +++-- doc/articles/scripts.md | 70 +-- doc/articles/troubleshooting.md | 51 ++- doc/articles/vendor-binaries.md | 41 +- ...ckage-to-a-custom-path-for-my-framework.md | 26 +- 14 files changed, 976 insertions(+), 719 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index fbb8c3613..343546269 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -33,11 +33,13 @@ You decide to use [monolog](https://github.com/Seldaek/monolog). In order to add it to your project, all you need to do is create a `composer.json` file which describes the project's dependencies. - { - "require": { - "monolog/monolog": "1.2.*" - } +```json +{ + "require": { + "monolog/monolog": "1.2.*" } +} +``` We are simply stating that our project requires some `monolog/monolog` package, any version beginning with `1.2`. @@ -63,12 +65,16 @@ Linux and OSX. To actually get Composer, we need to do two things. The first one is installing Composer (again, this means downloading it into your project): - $ curl -sS https://getcomposer.org/installer | php - +```sh +curl -sS https://getcomposer.org/installer | php +``` + > **Note:** If the above fails for some reason, you can download the installer > with `php` instead: - $ php -r "readfile('https://getcomposer.org/installer');" | php +```sh +php -r "readfile('https://getcomposer.org/installer');" | php +``` This will just check a few PHP settings and then download `composer.phar` to your working directory. This file is the Composer binary. It is a PHAR (PHP @@ -78,7 +84,9 @@ line, amongst other things. You can install Composer to a specific directory by using the `--install-dir` option and providing a target directory (it can be an absolute or relative path): - $ curl -sS https://getcomposer.org/installer | php -- --install-dir=bin +```sh +curl -sS https://getcomposer.org/installer | php -- --install-dir=bin +``` #### Globally @@ -88,8 +96,10 @@ executable and invoke it without `php`. You can run these commands to easily access `composer` from anywhere on your system: - $ curl -sS https://getcomposer.org/installer | php - $ mv composer.phar /usr/local/bin/composer +```sh +curl -sS https://getcomposer.org/installer | php +mv composer.phar /usr/local/bin/composer +``` > **Note:** If the above fails due to permissions, run the `mv` line > again with sudo. @@ -123,21 +133,25 @@ just call `composer` from any directory in your command line. Change to a directory on your `PATH` and run the install snippet to download composer.phar: - C:\Users\username>cd C:\bin - C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php +```sh +C:\Users\username>cd C:\bin +C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php +``` > **Note:** If the above fails due to readfile, use the `http` url or enable php_openssl.dll in php.ini Create a new `composer.bat` file alongside `composer.phar`: - C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat +```sh +C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat +``` Close your current terminal. Test usage with a new terminal: - C:\Users\username>composer -V - Composer version 27d8904 - - C:\Users\username> +```sh +C:\Users\username>composer -V +Composer version 27d8904 +``` ## Using Composer @@ -147,12 +161,16 @@ don't have a `composer.json` file in the current directory please skip to the To resolve and download dependencies, run the `install` command: - $ php composer.phar install +```sh +php composer.phar install +``` If you did a global install and do not have the phar in that directory run this instead: - $ composer install +```sh +composer install +``` Following the [example above](#declaring-dependencies), this will download monolog into the `vendor/monolog/monolog` directory. @@ -164,7 +182,9 @@ capable of autoloading all of the classes in any of the libraries that it downloads. To use it, just add the following line to your code's bootstrap process: - require 'vendor/autoload.php'; +```php +require 'vendor/autoload.php'; +``` Woah! Now start using monolog! To keep learning more about Composer, keep reading the "Basic Usage" chapter. diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 8ac5066fc..270a6d50a 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -4,20 +4,26 @@ To install Composer, you just need to download the `composer.phar` executable. - $ curl -sS https://getcomposer.org/installer | php +```sh +curl -sS https://getcomposer.org/installer | php +``` For the details, see the [Introduction](00-intro.md) chapter. To check if Composer is working, just run the PHAR through `php`: - $ php composer.phar +```sh +php composer.phar +``` This should give you a list of available commands. > **Note:** You can also perform the checks only without downloading Composer > by using the `--check` option. For more information, just use `--help`. > -> $ curl -sS https://getcomposer.org/installer | php -- --help +> ```sh +> curl -sS https://getcomposer.org/installer | php -- --help +> ``` ## `composer.json`: Project Setup @@ -34,11 +40,13 @@ The first (and often only) thing you specify in `composer.json` is the `require` key. You're simply telling Composer which packages your project depends on. - { - "require": { - "monolog/monolog": "1.0.*" - } +```json +{ + "require": { + "monolog/monolog": "1.0.*" } +} +``` As you can see, `require` takes an object that maps **package names** (e.g. `monolog/monolog`) to **package versions** (e.g. `1.0.*`). @@ -99,7 +107,9 @@ packages instead of doing per dependency you can also use the To fetch the defined dependencies into your local project, just run the `install` command of `composer.phar`. - $ php composer.phar install +```sh +php composer.phar install +``` This will find the latest version of `monolog/monolog` that matches the supplied version constraint and download it into the `vendor` directory. @@ -141,11 +151,15 @@ automatically. To update to the new version, use `update` command. This will fet the latest matching versions (according to your `composer.json` file) and also update the lock file with the new version. - $ php composer.phar update +```sh +php composer.phar update +``` If you only want to install or update one dependency, you can whitelist them: - $ php composer.phar update monolog/monolog [...] +```sh +php composer.phar update monolog/monolog [...] +``` > **Note:** For libraries it is not necessarily recommended to commit the lock file, > see also: [Libraries - Lock file](02-libraries.md#lock-file). @@ -171,25 +185,31 @@ For libraries that specify autoload information, Composer generates a `vendor/autoload.php` file. You can simply include this file and you will get autoloading for free. - require 'vendor/autoload.php'; +```php +require 'vendor/autoload.php'; +``` This makes it really easy to use third party code. For example: If your project depends on monolog, you can just start using classes from it, and they will be autoloaded. - $log = new Monolog\Logger('name'); - $log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING)); +```php +$log = new Monolog\Logger('name'); +$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING)); - $log->addWarning('Foo'); +$log->addWarning('Foo'); +``` You can even add your own code to the autoloader by adding an `autoload` field to `composer.json`. - { - "autoload": { - "psr-4": {"Acme\\": "src/"} - } +```json +{ + "autoload": { + "psr-4": {"Acme\\": "src/"} } +} +``` Composer will register a [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloader for the `Acme` namespace. @@ -205,8 +225,10 @@ Including that file will also return the autoloader instance, so you can store the return value of the include call in a variable and add more namespaces. This can be useful for autoloading classes in a test suite, for example. - $loader = require 'vendor/autoload.php'; - $loader->add('Acme\\Test\\', __DIR__); +```php +$loader = require 'vendor/autoload.php'; +$loader->add('Acme\\Test\\', __DIR__); +``` In addition to PSR-4 autoloading, classmap is also supported. This allows classes to be autoloaded even if they do not conform to PSR-4. See the diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 10612c660..81ec3edaa 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -12,12 +12,14 @@ libraries is that your project is a package without a name. In order to make that package installable you need to give it a name. You do this by adding a `name` to `composer.json`: - { - "name": "acme/hello-world", - "require": { - "monolog/monolog": "1.0.*" - } +```json +{ + "name": "acme/hello-world", + "require": { + "monolog/monolog": "1.0.*" } +} +``` In this case the project name is `acme/hello-world`, where `acme` is the vendor name. Supplying a vendor name is mandatory. @@ -62,9 +64,11 @@ version numbers are extracted from these. If you are creating packages by hand and really have to specify it explicitly, you can just add a `version` field: - { - "version": "1.0.0" - } +```json +{ + "version": "1.0.0" +} +``` > **Note:** You should avoid specifying the version field explicitly, because > for tags the value must match the tag name. @@ -78,12 +82,12 @@ a number. Here are a few examples of valid tag names: - 1.0.0 - v1.0.0 - 1.10.5-RC1 - v4.4.4beta2 - v2.0.0-alpha - v2.0.4-p1 +- 1.0.0 +- v1.0.0 +- 1.10.5-RC1 +- v4.4.4beta2 +- v2.0.0-alpha +- v2.0.4-p1 > **Note:** Even if your tag is prefixed with `v`, a [version constraint](01-basic-usage.md#package-versions) > in a `require` statement has to be specified without prefix @@ -101,9 +105,9 @@ like a version, it will be `dev-{branchname}`. `master` results in a Here are some examples of version branch names: - 1.x - 1.0 (equals 1.0.x) - 1.1.x +- 1.x +- 1.0 (equals 1.0.x) +- 1.1.x > **Note:** When you install a development version, it will be automatically > pulled from its `source`. See the [`install`](03-cli.md#install) command @@ -140,12 +144,14 @@ project locally. We will call it `acme/blog`. This blog will depend on accomplish this by creating a new `blog` directory somewhere, containing a `composer.json`: - { - "name": "acme/blog", - "require": { - "acme/hello-world": "dev-master" - } +```json +{ + "name": "acme/blog", + "require": { + "acme/hello-world": "dev-master" } +} +``` The name is not needed in this case, since we don't want to publish the blog as a library. It is added here to clarify which `composer.json` is being @@ -155,18 +161,20 @@ Now we need to tell the blog app where to find the `hello-world` dependency. We do this by adding a package repository specification to the blog's `composer.json`: - { - "name": "acme/blog", - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/username/hello-world" - } - ], - "require": { - "acme/hello-world": "dev-master" +```json +{ + "name": "acme/blog", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/username/hello-world" } + ], + "require": { + "acme/hello-world": "dev-master" } +} +``` For more details on how package repositories work and what other types are available, see [Repositories](05-repositories.md). diff --git a/doc/03-cli.md b/doc/03-cli.md index acbd470e2..517c9648a 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -36,7 +36,9 @@ it a bit easier to do this. When you run the command it will interactively ask you to fill in the fields, while using some smart defaults. - $ php composer.phar init +```sh +php composer.phar init +``` ### Options @@ -54,7 +56,9 @@ while using some smart defaults. The `install` command reads the `composer.json` file from the current directory, resolves the dependencies, and installs them into `vendor`. - $ php composer.phar install +```sh +php composer.phar install +``` If there is a `composer.lock` file in the current directory, it will use the exact versions from there instead of resolving them. This ensures that @@ -94,18 +98,24 @@ resolution. In order to get the latest versions of the dependencies and to update the `composer.lock` file, you should use the `update` command. - $ php composer.phar update +```sh +php composer.phar update +``` This will resolve all dependencies of the project and write the exact versions into `composer.lock`. If you just want to update a few packages and not all, you can list them as such: - $ php composer.phar update vendor/package vendor/package2 +```sh +php composer.phar update vendor/package vendor/package2 +``` You can also use wildcards to update a bunch of packages at once: - $ php composer.phar update vendor/* +```sh +php composer.phar update vendor/* +``` ### Options @@ -131,7 +141,9 @@ You can also use wildcards to update a bunch of packages at once: The `require` command adds new packages to the `composer.json` file from the current directory. - $ php composer.phar require +```sh +php composer.phar require +``` After adding/changing the requirements, the modified requirements will be installed or updated. @@ -139,7 +151,9 @@ installed or updated. If you do not want to choose requirements interactively, you can just pass them to the command. - $ php composer.phar require vendor/package:2.* vendor/package2:dev-master +```sh +php composer.phar require vendor/package:2.* vendor/package2:dev-master +``` ### Options @@ -162,13 +176,17 @@ This can be used to install CLI utilities globally and if you add `$COMPOSER_HOME/vendor/bin` to your `$PATH` environment variable. Here is an example: - $ php composer.phar global require fabpot/php-cs-fixer:dev-master +```sh +php composer.phar global require fabpot/php-cs-fixer:dev-master +``` Now the `php-cs-fixer` binary is available globally (assuming you adjusted your PATH). If you wish to update the binary later on you can just run a global update: - $ php composer.phar global update +```sh +php composer.phar global update +``` ## search @@ -176,7 +194,9 @@ The search command allows you to search through the current project's package repositories. Usually this will be just packagist. You simply pass it the terms you want to search for. - $ php composer.phar search monolog +```sh +php composer.phar search monolog +``` You can also search for more than one term by passing multiple arguments. @@ -188,32 +208,38 @@ You can also search for more than one term by passing multiple arguments. To list all of the available packages, you can use the `show` command. - $ php composer.phar show +```sh +php composer.phar show +``` If you want to see the details of a certain package, you can pass the package name. - $ php composer.phar show monolog/monolog +```sh +php composer.phar show monolog/monolog - name : monolog/monolog - versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1 - type : library - names : monolog/monolog - source : [git] http://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da - dist : [zip] http://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da - license : MIT +name : monolog/monolog +versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1 +type : library +names : monolog/monolog +source : [git] http://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da +dist : [zip] http://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da +license : MIT - autoload - psr-0 - Monolog : src/ +autoload +psr-0 +Monolog : src/ - requires - php >=5.3.0 +requires +php >=5.3.0 +``` You can even pass the package version, which will tell you the details of that specific version. - $ php composer.phar show monolog/monolog 1.0.2 +```sh +php composer.phar show monolog/monolog 1.0.2 +``` ### Options @@ -227,13 +253,15 @@ The `depends` command tells you which other packages depend on a certain package. You can specify which link types (`require`, `require-dev`) should be included in the listing. By default both are used. - $ php composer.phar depends --link-type=require monolog/monolog +```sh +php composer.phar depends --link-type=require monolog/monolog - nrk/monolog-fluent - poc/poc - propel/propel - symfony/monolog-bridge - symfony/symfony +nrk/monolog-fluent +poc/poc +propel/propel +symfony/monolog-bridge +symfony/symfony +``` ### Options @@ -246,7 +274,9 @@ You should always run the `validate` command before you commit your `composer.json` file, and before you tag a release. It will check if your `composer.json` is valid. - $ php composer.phar validate +```sh +php composer.phar validate +``` ### Options @@ -258,31 +288,42 @@ If you often need to modify the code of your dependencies and they are installed from source, the `status` command allows you to check if you have local changes in any of them. - $ php composer.phar status +```sh +php composer.phar status +``` With the `--verbose` option you get some more information about what was changed: - $ php composer.phar status -v - You have changes in the following dependencies: - vendor/seld/jsonlint: - M README.mdown +```sh +php composer.phar status -v + +You have changes in the following dependencies: +vendor/seld/jsonlint: + M README.mdown +``` ## self-update To update composer itself to the latest version, just run the `self-update` command. It will replace your `composer.phar` with the latest version. - $ php composer.phar self-update +```sh +php composer.phar self-update +``` If you would like to instead update to a specific release simply specify it: - $ composer self-update 1.0.0-alpha7 +```sh +php composer.phar self-update 1.0.0-alpha7 +``` If you have installed composer for your entire system (see [global installation](00-intro.md#globally)), you may have to run the command with `root` privileges - $ sudo composer self-update +```sh +sudo composer self-update +``` ### Options @@ -294,7 +335,9 @@ you may have to run the command with `root` privileges The `config` command allows you to edit some basic composer settings in either the local composer.json file or the global config.json file. - $ php composer.phar config --list +```sh +php composer.phar config --list +``` ### Usage @@ -326,7 +369,9 @@ the global config file. In addition to modifying the config section, the `config` command also supports making changes to the repositories section by using it the following way: - $ php composer.phar config repositories.foo vcs http://github.com/foo/bar +```sh +php composer.phar config repositories.foo vcs http://github.com/foo/bar +``` ## create-project @@ -347,7 +392,9 @@ provide a version as third argument, otherwise the latest version is used. If the directory does not currently exist, it will be created during installation. - php composer.phar create-project doctrine/orm path 2.2.* +```sh +php composer.phar create-project doctrine/orm path 2.2.* +``` It is also possible to run the command without params in a directory with an existing `composer.json` file to bootstrap a project. @@ -409,7 +456,9 @@ If you think you found a bug, or something is behaving strangely, you might want to run the `diagnose` command to perform automated checks for many common problems. - $ php composer.phar diagnose +```sh +php composer.phar diagnose +``` ## archive @@ -417,7 +466,9 @@ This command is used to generate a zip/tar archive for a given package in a given version. It can also be used to archive your entire project without excluded/ignored files. - $ php composer.phar archive vendor/package 2.0.21 --format=zip +```sh +php composer.phar archive vendor/package 2.0.21 --format=zip +``` ### Options @@ -429,7 +480,9 @@ excluded/ignored files. To get more information about a certain command, just use `help`. - $ php composer.phar help install +```sh +php composer.phar help install +``` ## Environment variables @@ -445,7 +498,9 @@ By setting the `COMPOSER` env variable it is possible to set the filename of For example: - $ COMPOSER=composer-other.json php composer.phar install +```sh +COMPOSER=composer-other.json php composer.phar install +``` ### COMPOSER_ROOT_VERSION diff --git a/doc/04-schema.md b/doc/04-schema.md index 906075372..0ce4d4fdb 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -59,14 +59,14 @@ RC suffixes can also be followed by a number. Examples: - 1.0.0 - 1.0.2 - 1.1.0 - 0.2.5 - 1.0.0-dev - 1.0.0-alpha3 - 1.0.0-beta2 - 1.0.0-RC5 +- 1.0.0 +- 1.0.2 +- 1.1.0 +- 0.2.5 +- 1.0.0-dev +- 1.0.0-alpha3 +- 1.0.0-beta2 +- 1.0.0-RC5 Optional if the package repository can infer the version from somewhere, such as the VCS tag name in the VCS repository. In that case it is also recommended @@ -113,11 +113,11 @@ searching and filtering. Examples: - logging - events - database - redis - templating +- logging +- events +- database +- redis +- templating Optional. @@ -141,19 +141,19 @@ The license of the package. This can be either a string or an array of strings. The recommended notation for the most common licenses is (alphabetical): - Apache-2.0 - BSD-2-Clause - BSD-3-Clause - BSD-4-Clause - GPL-2.0 - GPL-2.0+ - GPL-3.0 - GPL-3.0+ - LGPL-2.1 - LGPL-2.1+ - LGPL-3.0 - LGPL-3.0+ - MIT +- Apache-2.0 +- BSD-2-Clause +- BSD-3-Clause +- BSD-4-Clause +- GPL-2.0 +- GPL-2.0+ +- GPL-3.0 +- GPL-3.0+ +- LGPL-2.1 +- LGPL-2.1+ +- LGPL-3.0 +- LGPL-3.0+ +- MIT Optional, but it is highly recommended to supply this. More identifiers are listed at the [SPDX Open Source License Registry](http://www.spdx.org/licenses/). @@ -162,28 +162,33 @@ For closed-source software, you may use `"proprietary"` as the license identifie An Example: - { - "license": "MIT" - } - +```json +{ + "license": "MIT" +} +``` For a package, when there is a choice between licenses ("disjunctive license"), multiple can be specified as array. An Example for disjunctive licenses: - { - "license": [ - "LGPL-2.1", - "GPL-3.0+" - ] - } +```json +{ + "license": [ + "LGPL-2.1", + "GPL-3.0+" + ] +} +``` Alternatively they can be separated with "or" and enclosed in parenthesis; - { - "license": "(LGPL-2.1 or GPL-3.0+)" - } +```json +{ + "license": "(LGPL-2.1 or GPL-3.0+)" +} +``` Similarly when multiple licenses need to be applied ("conjunctive license"), they should be separated with "and" and enclosed in parenthesis. @@ -201,22 +206,24 @@ Each author object can have following properties: An example: - { - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de", - "role": "Developer" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be", - "role": "Developer" - } - ] - } +```json +{ + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de", + "role": "Developer" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + } + ] +} +``` Optional, but highly recommended. @@ -235,12 +242,14 @@ Support information includes the following: An example: - { - "support": { - "email": "support@example.org", - "irc": "irc://irc.freenode.org/composer" - } +```json +{ + "support": { + "email": "support@example.org", + "irc": "irc://irc.freenode.org/composer" } +} +``` Optional. @@ -251,11 +260,13 @@ All of the following take an object which maps package names to Example: - { - "require": { - "monolog/monolog": "1.0.*" - } +```json +{ + "require": { + "monolog/monolog": "1.0.*" } +} +``` All links are optional fields. @@ -267,24 +278,28 @@ allow unstable packages of a dependency for example. Example: - { - "require": { - "monolog/monolog": "1.0.*@beta", - "acme/foo": "@dev" - } +```json +{ + "require": { + "monolog/monolog": "1.0.*@beta", + "acme/foo": "@dev" } +} +``` If one of your dependencies has a dependency on an unstable package you need to explicitly require it as well, along with its sufficient stability flag. Example: - { - "require": { - "doctrine/doctrine-fixtures-bundle": "dev-master", - "doctrine/data-fixtures": "@dev" - } +```json +{ + "require": { + "doctrine/doctrine-fixtures-bundle": "dev-master", + "doctrine/data-fixtures": "@dev" } +} +``` `require` and `require-dev` additionally support explicit references (i.e. commit) for dev versions to make sure they are locked to a given state, even @@ -293,12 +308,14 @@ and append the reference with `#`. Example: - { - "require": { - "monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7", - "acme/foo": "1.0.x-dev#abc123" - } +```json +{ + "require": { + "monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7", + "acme/foo": "1.0.x-dev#abc123" } +} +``` > **Note:** While this is convenient at times, it should not be how you use > packages in the long term because it comes with a technical limitation. The @@ -370,11 +387,13 @@ and not version constraints. Example: - { - "suggest": { - "monolog/monolog": "Allows more advanced logging of the application flow" - } +```json +{ + "suggest": { + "monolog/monolog": "Allows more advanced logging of the application flow" } +} +``` ### autoload @@ -403,32 +422,38 @@ key => value array which may be found in the generated file Example: - { - "autoload": { - "psr-4": { - "Monolog\\": "src/", - "Vendor\\Namespace\\": "" - } +```json +{ + "autoload": { + "psr-4": { + "Monolog\\": "src/", + "Vendor\\Namespace\\": "" } } +} +``` If you need to search for a same prefix in multiple directories, you can specify them as an array as such: - { - "autoload": { - "psr-4": { "Monolog\\": ["src/", "lib/"] } - } +```json +{ + "autoload": { + "psr-4": { "Monolog\\": ["src/", "lib/"] } } +} +``` If you want to have a fallback directory where any namespace will be looked for, you can use an empty prefix like: - { - "autoload": { - "psr-4": { "": "src/" } - } +```json +{ + "autoload": { + "psr-4": { "": "src/" } } +} +``` #### PSR-0 @@ -444,44 +469,52 @@ array which may be found in the generated file `vendor/composer/autoload_namespa Example: - { - "autoload": { - "psr-0": { - "Monolog\\": "src/", - "Vendor\\Namespace\\": "src/", - "Vendor_Namespace_": "src/" - } +```json +{ + "autoload": { + "psr-0": { + "Monolog\\": "src/", + "Vendor\\Namespace\\": "src/", + "Vendor_Namespace_": "src/" } } +} +``` If you need to search for a same prefix in multiple directories, you can specify them as an array as such: - { - "autoload": { - "psr-0": { "Monolog\\": ["src/", "lib/"] } - } +```json +{ + "autoload": { + "psr-0": { "Monolog\\": ["src/", "lib/"] } } +} +``` The PSR-0 style is not limited to namespace declarations only but may be specified right down to the class level. This can be useful for libraries with only one class in the global namespace. If the php source file is also located in the root of the package, for example, it may be declared like this: - { - "autoload": { - "psr-0": { "UniqueGlobalClass": "" } - } +```json +{ + "autoload": { + "psr-0": { "UniqueGlobalClass": "" } } +} +``` If you want to have a fallback directory where any namespace can be, you can use an empty prefix like: - { - "autoload": { - "psr-0": { "": "src/" } - } +```json +{ + "autoload": { + "psr-0": { "": "src/" } } +} +``` #### Classmap @@ -496,11 +529,13 @@ to search for classes. Example: - { - "autoload": { - "classmap": ["src/", "lib/", "Something.php"] - } +```json +{ + "autoload": { + "classmap": ["src/", "lib/", "Something.php"] } +} +``` #### Files @@ -510,11 +545,13 @@ that cannot be autoloaded by PHP. Example: - { - "autoload": { - "files": ["src/MyLibrary/functions.php"] - } +```json +{ + "autoload": { + "files": ["src/MyLibrary/functions.php"] } +} +``` ### autoload-dev (root-only) @@ -529,14 +566,16 @@ and to add it within the autoload-dev section. Example: - { - "autoload": { - "psr-4": { "MyLibrary\\": "src/" } - }, - "autoload-dev": { - "psr-4": { "MyLibrary\\Tests\\": "tests/" } - } +```json +{ + "autoload": { + "psr-4": { "MyLibrary\\": "src/" } + }, + "autoload-dev": { + "psr-4": { "MyLibrary\\Tests\\": "tests/" } } +} +``` ### include-path @@ -548,9 +587,11 @@ A list of paths which should get appended to PHP's `include_path`. Example: - { - "include-path": ["lib/"] - } +```json +{ + "include-path": ["lib/"] +} +``` Optional. @@ -574,12 +615,14 @@ it from `vendor/symfony/yaml`. To do that, `autoload` and `target-dir` are defined as follows: - { - "autoload": { - "psr-0": { "Symfony\\Component\\Yaml\\": "" } - }, - "target-dir": "Symfony/Component/Yaml" - } +```json +{ + "autoload": { + "psr-0": { "Symfony\\Component\\Yaml\\": "" } + }, + "target-dir": "Symfony/Component/Yaml" +} +``` Optional. @@ -637,47 +680,49 @@ For more information on any of these, see [Repositories](05-repositories.md). Example: - { - "repositories": [ - { - "type": "composer", - "url": "http://packages.example.com" - }, - { - "type": "composer", - "url": "https://packages.example.com", - "options": { - "ssl": { - "verify_peer": "true" - } - } - }, - { - "type": "vcs", - "url": "https://github.com/Seldaek/monolog" - }, - { - "type": "pear", - "url": "http://pear2.php.net" - }, - { - "type": "package", - "package": { - "name": "smarty/smarty", - "version": "3.1.7", - "dist": { - "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", - "type": "zip" - }, - "source": { - "url": "http://smarty-php.googlecode.com/svn/", - "type": "svn", - "reference": "tags/Smarty_3_1_7/distribution/" - } +```json +{ + "repositories": [ + { + "type": "composer", + "url": "http://packages.example.com" + }, + { + "type": "composer", + "url": "https://packages.example.com", + "options": { + "ssl": { + "verify_peer": "true" } } - ] - } + }, + { + "type": "vcs", + "url": "https://github.com/Seldaek/monolog" + }, + { + "type": "pear", + "url": "http://pear2.php.net" + }, + { + "type": "package", + "package": { + "name": "smarty/smarty", + "version": "3.1.7", + "dist": { + "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", + "type": "zip" + }, + "source": { + "url": "http://smarty-php.googlecode.com/svn/", + "type": "svn", + "reference": "tags/Smarty_3_1_7/distribution/" + } + } + } + ] +} +``` > **Note:** Order is significant here. When looking for a package, Composer will look from the first to the last repository, and pick the first match. @@ -749,11 +794,13 @@ The following options are supported: Example: - { - "config": { - "bin-dir": "bin" - } +```json +{ + "config": { + "bin-dir": "bin" } +} +``` ### scripts (root-only) @@ -769,7 +816,9 @@ Arbitrary extra data for consumption by `scripts`. This can be virtually anything. To access it from within a script event handler, you can do: - $extra = $event->getComposer()->getPackage()->getExtra(); +```php +$extra = $event->getComposer()->getPackage()->getExtra(); +``` Optional. @@ -796,11 +845,13 @@ The following options are supported: Example: - { - "archive": { - "exclude": ["/foo/bar", "baz", "/*.test", "!/foo/bar/baz"] - } +```json +{ + "archive": { + "exclude": ["/foo/bar", "baz", "/*.test", "!/foo/bar/baz"] } +} +``` The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`, `/foo/my.test` but it will exclude `/foo/bar/any`, `/foo/baz`, and `/my.test`. diff --git a/doc/05-repositories.md b/doc/05-repositories.md index a4e92d7ed..049a05170 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -66,16 +66,18 @@ repository URL would be `example.org`. The only required field is `packages`. The JSON structure is as follows: - { - "packages": { - "vendor/package-name": { - "dev-master": { @composer.json }, - "1.0.x-dev": { @composer.json }, - "0.0.1": { @composer.json }, - "1.0.0": { @composer.json } - } +```json +{ + "packages": { + "vendor/package-name": { + "dev-master": { @composer.json }, + "1.0.x-dev": { @composer.json }, + "0.0.1": { @composer.json }, + "1.0.0": { @composer.json } } } +} +``` The `@composer.json` marker would be the contents of the `composer.json` from that package version including as a minimum: @@ -86,14 +88,16 @@ that package version including as a minimum: Here is a minimal package definition: - { - "name": "smarty/smarty", - "version": "3.1.7", - "dist": { - "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", - "type": "zip" - } +```json +{ + "name": "smarty/smarty", + "version": "3.1.7", + "dist": { + "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", + "type": "zip" } +} +``` It may include any of the other fields specified in the [schema](04-schema.md). @@ -105,19 +109,23 @@ every time a user installs a package. The URL can be either an absolute path An example value: - { - "notify-batch": "/downloads/" - } +```json +{ + "notify-batch": "/downloads/" +} +``` For `example.org/packages.json` containing a `monolog/monolog` package, this would send a `POST` request to `example.org/downloads/` with following JSON request body: - { - "downloads": [ - {"name": "monolog/monolog", "version": "1.2.1.0"}, - ] - } +```json +{ + "downloads": [ + {"name": "monolog/monolog", "version": "1.2.1.0"}, + ] +} +``` The version field will contain the normalized representation of the version number. @@ -132,19 +140,21 @@ files. An example: - { - "includes": { - "packages-2011.json": { - "sha1": "525a85fb37edd1ad71040d429928c2c0edec9d17" - }, - "packages-2012-01.json": { - "sha1": "897cde726f8a3918faf27c803b336da223d400dd" - }, - "packages-2012-02.json": { - "sha1": "26f911ad717da26bbcac3f8f435280d13917efa5" - } +```json +{ + "includes": { + "packages-2011.json": { + "sha1": "525a85fb37edd1ad71040d429928c2c0edec9d17" + }, + "packages-2012-01.json": { + "sha1": "897cde726f8a3918faf27c803b336da223d400dd" + }, + "packages-2012-02.json": { + "sha1": "26f911ad717da26bbcac3f8f435280d13917efa5" } } +} +``` The SHA-1 sum of the file allows it to be cached and only re-requested if the hash changed. @@ -164,31 +174,35 @@ is an absolute path from the repository root. An example: - { - "provider-includes": { - "providers-a.json": { - "sha256": "f5b4bc0b354108ef08614e569c1ed01a2782e67641744864a74e788982886f4c" - }, - "providers-b.json": { - "sha256": "b38372163fac0573053536f5b8ef11b86f804ea8b016d239e706191203f6efac" - } +```json +{ + "provider-includes": { + "providers-a.json": { + "sha256": "f5b4bc0b354108ef08614e569c1ed01a2782e67641744864a74e788982886f4c" }, - "providers-url": "/p/%package%$%hash%.json" - } + "providers-b.json": { + "sha256": "b38372163fac0573053536f5b8ef11b86f804ea8b016d239e706191203f6efac" + } + }, + "providers-url": "/p/%package%$%hash%.json" +} +``` Those files contain lists of package names and hashes to verify the file integrity, for example: - { - "providers": { - "acme/foo": { - "sha256": "38968de1305c2e17f4de33aea164515bc787c42c7e2d6e25948539a14268bb82" - }, - "acme/bar": { - "sha256": "4dd24c930bd6e1103251306d6336ac813b563a220d9ca14f4743c032fb047233" - } +```json +{ + "providers": { + "acme/foo": { + "sha256": "38968de1305c2e17f4de33aea164515bc787c42c7e2d6e25948539a14268bb82" + }, + "acme/bar": { + "sha256": "4dd24c930bd6e1103251306d6336ac813b563a220d9ca14f4743c032fb047233" } } +} +``` The file above declares that acme/foo and acme/bar can be found in this repository, by loading the file referenced by `providers-url`, replacing @@ -225,17 +239,19 @@ point to your custom branch. For version constraint naming conventions see Example assuming you patched monolog to fix a bug in the `bugfix` branch: - { - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/igorw/monolog" - } - ], - "require": { - "monolog/monolog": "dev-bugfix" +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/igorw/monolog" } + ], + "require": { + "monolog/monolog": "dev-bugfix" } +} +``` When you run `php composer.phar update`, you should get your modified version of `monolog/monolog` instead of the one from packagist. @@ -256,17 +272,19 @@ For more information [see the aliases article](articles/aliases.md). Exactly the same solution allows you to work with your private repositories at GitHub and BitBucket: - { - "require": { - "vendor/my-private-repo": "dev-master" - }, - "repositories": [ - { - "type": "vcs", - "url": "git@bitbucket.org:vendor/my-private-repo.git" - } - ] - } +```json +{ + "require": { + "vendor/my-private-repo": "dev-master" + }, + "repositories": [ + { + "type": "vcs", + "url": "git@bitbucket.org:vendor/my-private-repo.git" + } + ] +} +``` The only requirement is the installation of SSH keys for a git client. @@ -305,17 +323,19 @@ by default that code is located in `$url/trunk`, `$url/branches` and values. For example if you used capitalized names you could configure the repository like this: - { - "repositories": [ - { - "type": "vcs", - "url": "http://svn.example.org/projectA/", - "trunk-path": "Trunk", - "branches-path": "Branches", - "tags-path": "Tags" - } - ] - } +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "http://svn.example.org/projectA/", + "trunk-path": "Trunk", + "branches-path": "Branches", + "tags-path": "Tags" + } + ] +} +``` If you have no branches or tags directory you can disable them entirely by setting the `branches-path` or `tags-path` to `false`. @@ -333,18 +353,20 @@ avoid conflicts. All packages are also aliased with prefix `pear-{channelAlias}/ Example using `pear2.php.net`: - { - "repositories": [ - { - "type": "pear", - "url": "http://pear2.php.net" - } - ], - "require": { - "pear-pear2.php.net/PEAR2_Text_Markdown": "*", - "pear-pear2/PEAR2_HTTP_Request": "*" +```json +{ + "repositories": [ + { + "type": "pear", + "url": "http://pear2.php.net" } + ], + "require": { + "pear-pear2.php.net/PEAR2_Text_Markdown": "*", + "pear-pear2/PEAR2_HTTP_Request": "*" } +} +``` In this case the short name of the channel is `pear2`, so the `PEAR2_HTTP_Request` package name becomes `pear-pear2/PEAR2_HTTP_Request`. @@ -387,23 +409,25 @@ To illustrate, the following example would get the `BasePackage`, `TopLevelPackage1`, and `TopLevelPackage2` packages from your PEAR repository and `IntermediatePackage` from a Github repository: - { - "repositories": [ - { - "type": "git", - "url": "https://github.com/foobar/intermediate.git" - }, - { - "type": "pear", - "url": "http://pear.foobar.repo", - "vendor-alias": "foobar" - } - ], - "require": { - "foobar/TopLevelPackage1": "*", - "foobar/TopLevelPackage2": "*" +```json +{ + "repositories": [ + { + "type": "git", + "url": "https://github.com/foobar/intermediate.git" + }, + { + "type": "pear", + "url": "http://pear.foobar.repo", + "vendor-alias": "foobar" } + ], + "require": { + "foobar/TopLevelPackage1": "*", + "foobar/TopLevelPackage2": "*" } +} +``` ### Package @@ -418,32 +442,34 @@ minimum required fields are `name`, `version`, and either of `dist` or Here is an example for the smarty template engine: - { - "repositories": [ - { - "type": "package", - "package": { - "name": "smarty/smarty", - "version": "3.1.7", - "dist": { - "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", - "type": "zip" - }, - "source": { - "url": "http://smarty-php.googlecode.com/svn/", - "type": "svn", - "reference": "tags/Smarty_3_1_7/distribution/" - }, - "autoload": { - "classmap": ["libs/"] - } +```json +{ + "repositories": [ + { + "type": "package", + "package": { + "name": "smarty/smarty", + "version": "3.1.7", + "dist": { + "url": "http://www.smarty.net/files/Smarty-3.1.7.zip", + "type": "zip" + }, + "source": { + "url": "http://smarty-php.googlecode.com/svn/", + "type": "svn", + "reference": "tags/Smarty_3_1_7/distribution/" + }, + "autoload": { + "classmap": ["libs/"] } } - ], - "require": { - "smarty/smarty": "3.1.*" } + ], + "require": { + "smarty/smarty": "3.1.*" } +} +``` Typically you would leave the source part off, as you don't really need it. @@ -512,25 +538,30 @@ of the times they are private. To simplify maintenance, one can simply use a repository of type `artifact` with a folder containing ZIP archives of those private packages: - { - "repositories": [ - { - "type": "artifact", - "url": "path/to/directory/with/zips/" - } - ], - "require": { - "private-vendor-one/core": "15.6.2", - "private-vendor-two/connectivity": "*", - "acme-corp/parser": "10.3.5" +```json +{ + "repositories": [ + { + "type": "artifact", + "url": "path/to/directory/with/zips/" } + ], + "require": { + "private-vendor-one/core": "15.6.2", + "private-vendor-two/connectivity": "*", + "acme-corp/parser": "10.3.5" } +} +``` Each zip artifact is just a ZIP archive with `composer.json` in root folder: - $ unzip -l acme-corp-parser-10.3.5.zip - composer.json - ... +```sh +unzip -l acme-corp-parser-10.3.5.zip + +composer.json +... +``` If there are two archives with different versions of a package, they are both imported. When an archive with a newer version is added in the artifact folder @@ -542,13 +573,14 @@ update to the latest version. You can disable the default Packagist repository by adding this to your `composer.json`: - { - "repositories": [ - { - "packagist": false - } - ] - } - +```json +{ + "repositories": [ + { + "packagist": false + } + ] +} +``` ← [Schema](04-schema.md) | [Community](06-community.md) → diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index 9e8f3da89..2b436322f 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -28,13 +28,15 @@ someone will want the latest master dev version. Thus, Composer allows you to alias your `dev-master` branch to a `1.0.x-dev` version. It is done by specifying a `branch-alias` field under `extra` in `composer.json`: - { - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } +```json +{ + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" } } +} +``` The branch version must begin with `dev-` (non-comparable version), the alias must be a comparable dev version (i.e. start with numbers, and end with @@ -68,18 +70,20 @@ You are using `symfony/monolog-bundle` which requires `monolog/monolog` version Just add this to your project's root `composer.json`: - { - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/you/monolog" - } - ], - "require": { - "symfony/monolog-bundle": "2.0", - "monolog/monolog": "dev-bugfix as 1.0.x-dev" +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/you/monolog" } + ], + "require": { + "symfony/monolog-bundle": "2.0", + "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/doc/articles/custom-installers.md b/doc/articles/custom-installers.md index feeebe52c..98a9a2212 100644 --- a/doc/articles/custom-installers.md +++ b/doc/articles/custom-installers.md @@ -34,13 +34,15 @@ An example use-case would be: An example composer.json of such a template package would be: - { - "name": "phpdocumentor/template-responsive", - "type": "phpdocumentor-template", - "require": { - "phpdocumentor/template-installer-plugin": "*" - } +```json +{ + "name": "phpdocumentor/template-responsive", + "type": "phpdocumentor-template", + "require": { + "phpdocumentor/template-installer-plugin": "*" } +} +``` > **IMPORTANT**: to make sure that the template installer is present at the > time the template package is installed, template packages should require @@ -70,20 +72,22 @@ requirements: Example: - { - "name": "phpdocumentor/template-installer-plugin", - "type": "composer-plugin", - "license": "MIT", - "autoload": { - "psr-0": {"phpDocumentor\\Composer": "src/"} - }, - "extra": { - "class": "phpDocumentor\\Composer\\TemplateInstallerPlugin" - }, - "require": { - "composer-plugin-api": "1.0.0" - } +```json +{ + "name": "phpdocumentor/template-installer-plugin", + "type": "composer-plugin", + "license": "MIT", + "autoload": { + "psr-0": {"phpDocumentor\\Composer": "src/"} + }, + "extra": { + "class": "phpDocumentor\\Composer\\TemplateInstallerPlugin" + }, + "require": { + "composer-plugin-api": "1.0.0" } +} +``` ### The Plugin class @@ -96,20 +100,24 @@ autoloadable and matches the `extra.class` element in the package definition. Example: - namespace phpDocumentor\Composer; +```php +getInstallationManager()->addInstaller($installer); - } + $installer = new TemplateInstaller($io, $composer); + $composer->getInstallationManager()->addInstaller($installer); } +} +``` ### The Custom Installer class @@ -138,39 +146,43 @@ source for the exact signature): Example: - namespace phpDocumentor\Composer; +```php +getPrettyName(), 0, 23); - if ('phpdocumentor/template-' !== $prefix) { - throw new \InvalidArgumentException( - 'Unable to install template, phpdocumentor templates ' - .'should always start their package name with ' - .'"phpdocumentor/template-"' - ); - } - - return 'data/templates/'.substr($package->getPrettyName(), 23); + $prefix = substr($package->getPrettyName(), 0, 23); + if ('phpdocumentor/template-' !== $prefix) { + throw new \InvalidArgumentException( + 'Unable to install template, phpdocumentor templates ' + .'should always start their package name with ' + .'"phpdocumentor/template-"' + ); } - /** - * {@inheritDoc} - */ - public function supports($packageType) - { - return 'phpdocumentor-template' === $packageType; - } + return 'data/templates/'.substr($package->getPrettyName(), 23); } + /** + * {@inheritDoc} + */ + public function supports($packageType) + { + return 'phpdocumentor-template' === $packageType; + } +} +``` + The example demonstrates that it is quite simple to extend the [`Composer\Installer\LibraryInstaller`][5] class to strip a prefix (`phpdocumentor/template-`) and use the remaining part to assemble a completely diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 01fe63494..69da38196 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -25,34 +25,38 @@ repositories you defined. The default file Satis looks for is `satis.json` in the root of the repository. - { - "name": "My Repository", - "homepage": "http://packages.example.org", - "repositories": [ - { "type": "vcs", "url": "http://github.com/mycompany/privaterepo" }, - { "type": "vcs", "url": "http://svn.example.org/private/repo" }, - { "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" } - ], - "require-all": true - } +```json +{ + "name": "My Repository", + "homepage": "http://packages.example.org", + "repositories": [ + { "type": "vcs", "url": "http://github.com/mycompany/privaterepo" }, + { "type": "vcs", "url": "http://svn.example.org/private/repo" }, + { "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" } + ], + "require-all": true +} +``` If you want to cherry pick which packages you want, you can list all the packages you want to have in your satis repository inside the classic composer `require` key, using a `"*"` constraint to make sure all versions are selected, or another constraint if you want really specific versions. - { - "repositories": [ - { "type": "vcs", "url": "http://github.com/mycompany/privaterepo" }, - { "type": "vcs", "url": "http://svn.example.org/private/repo" }, - { "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" } - ], - "require": { - "company/package": "*", - "company/package2": "*", - "company/package3": "2.0.0" - } +```json +{ + "repositories": [ + { "type": "vcs", "url": "http://github.com/mycompany/privaterepo" }, + { "type": "vcs", "url": "http://svn.example.org/private/repo" }, + { "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" } + ], + "require": { + "company/package": "*", + "company/package2": "*", + "company/package3": "2.0.0" } +} +``` Once you did this, you just run `php bin/satis build `. For example `php bin/satis build config.json web/` would read the `config.json` @@ -80,14 +84,16 @@ everything should work smoothly. You don't need to copy all your repositories in every project anymore. Only that one unique repository that will update itself. - { - "repositories": [ { "type": "composer", "url": "http://packages.example.org/" } ], - "require": { - "company/package": "1.2.0", - "company/package2": "1.5.2", - "company/package3": "dev-master" - } +```json +{ + "repositories": [ { "type": "composer", "url": "http://packages.example.org/" } ], + "require": { + "company/package": "1.2.0", + "company/package2": "1.5.2", + "company/package3": "dev-master" } +} +``` ### Security @@ -97,39 +103,43 @@ connection options for the server. Example using a custom repository using SSH (requires the SSH2 PECL extension): - { - "repositories": [ - { - "type": "composer", - "url": "ssh2.sftp://example.org", - "options": { - "ssh2": { - "username": "composer", - "pubkey_file": "/home/composer/.ssh/id_rsa.pub", - "privkey_file": "/home/composer/.ssh/id_rsa" - } +```json +{ + "repositories": [ + { + "type": "composer", + "url": "ssh2.sftp://example.org", + "options": { + "ssh2": { + "username": "composer", + "pubkey_file": "/home/composer/.ssh/id_rsa.pub", + "privkey_file": "/home/composer/.ssh/id_rsa" } } - ] - } + } + ] +} +``` > **Tip:** See [ssh2 context options](http://www.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options) for more information. Example using HTTP over SSL using a client certificate: - { - "repositories": [ - { - "type": "composer", - "url": "https://example.org", - "options": { - "ssl": { - "local_cert": "/home/composer/.ssl/composer.pem" - } +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org", + "options": { + "ssl": { + "local_cert": "/home/composer/.ssl/composer.pem" } } - ] - } + } + ] +} +``` > **Tip:** See [ssl context options](http://www.php.net/manual/en/context.ssl.php) for more information. @@ -145,14 +155,16 @@ Subversion) will not have downloads available and thus installations usually tak To enable your satis installation to create downloads for all (Git, Mercurial and Subversion) your packages, add the following to your `satis.json`: - { - "archive": { - "directory": "dist", - "format": "tar", - "prefix-url": "https://amazing.cdn.example.org", - "skip-dev": true - } +```json +{ + "archive": { + "directory": "dist", + "format": "tar", + "prefix-url": "https://amazing.cdn.example.org", + "skip-dev": true } +} +``` #### Options explained @@ -178,10 +190,11 @@ It is possible to make satis automatically resolve and add all dependencies for with the Downloads functionality to have a complete local mirror of packages. Just add the following to your `satis.json`: - - { - "require-dependencies": true - } +```json +{ + "require-dependencies": true +} +``` When searching for packages, satis will attempt to resolve all the required packages from the listed repositories. Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`. diff --git a/doc/articles/plugins.md b/doc/articles/plugins.md index 75706f711..65884fd18 100644 --- a/doc/articles/plugins.md +++ b/doc/articles/plugins.md @@ -35,13 +35,15 @@ current composer plugin API version is 1.0.0. For example - { - "name": "my/plugin-package", - "type": "composer-plugin", - "require": { - "composer-plugin-api": "1.0.0" - } +```json +{ + "name": "my/plugin-package", + "type": "composer-plugin", + "require": { + "composer-plugin-api": "1.0.0" } +} +``` ### Plugin Class @@ -54,20 +56,24 @@ be read and all internal objects and state can be manipulated as desired. Example: - namespace phpDocumentor\Composer; +```php +getInstallationManager()->addInstaller($installer); - } + $installer = new TemplateInstaller($io, $composer); + $composer->getInstallationManager()->addInstaller($installer); } +} +``` ## Event Handler @@ -88,46 +94,50 @@ The events available for plugins are: Example: - namespace Naderman\Composer\AWS; +```php +composer = $composer; + $this->io = $io; + } - public function activate(Composer $composer, IOInterface $io) - { - $this->composer = $composer; - $this->io = $io; - } + public static function getSubscribedEvents() + { + return array( + PluginEvents::PRE_FILE_DOWNLOAD => array( + array('onPreFileDownload', 0) + ), + ); + } - public static function getSubscribedEvents() - { - return array( - PluginEvents::PRE_FILE_DOWNLOAD => array( - array('onPreFileDownload', 0) - ), - ); - } + public function onPreFileDownload(PreFileDownloadEvent $event) + { + $protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME); - public function onPreFileDownload(PreFileDownloadEvent $event) - { - $protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME); - - if ($protocol === 's3') { - $awsClient = new AwsClient($this->io, $this->composer->getConfig()); - $s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient); - $event->setRemoteFilesystem($s3RemoteFilesystem); - } + if ($protocol === 's3') { + $awsClient = new AwsClient($this->io, $this->composer->getConfig()); + $s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient); + $event->setRemoteFilesystem($s3RemoteFilesystem); } } +} +``` ## Using Plugins diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index ff9c8b5bc..ccd8caf44 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -67,48 +67,52 @@ autoload functionality. Script definition example: - { - "scripts": { - "post-update-cmd": "MyVendor\\MyClass::postUpdate", - "post-package-install": [ - "MyVendor\\MyClass::postPackageInstall" - ], - "post-install-cmd": [ - "MyVendor\\MyClass::warmCache", - "phpunit -c app/" - ] - } +```json +{ + "scripts": { + "post-update-cmd": "MyVendor\\MyClass::postUpdate", + "post-package-install": [ + "MyVendor\\MyClass::postPackageInstall" + ], + "post-install-cmd": [ + "MyVendor\\MyClass::warmCache", + "phpunit -c app/" + ] } +} +``` Using the previous definition example, here's the class `MyVendor\MyClass` that might be used to execute the PHP callbacks: - getComposer(); - // do stuff - } - - public static function postPackageInstall(Event $event) - { - $installedPackage = $event->getOperation()->getPackage(); - // do stuff - } - - public static function warmCache(Event $event) - { - // make cache toasty - } + $composer = $event->getComposer(); + // do stuff } + public static function postPackageInstall(Event $event) + { + $installedPackage = $event->getOperation()->getPackage(); + // do stuff + } + + public static function warmCache(Event $event) + { + // make cache toasty + } +} +``` + When an event is fired, Composer's internal event handler receives a `Composer\Script\Event` object, which is passed as the first argument to your PHP callback. This `Event` object has getters for other contextual objects: @@ -122,6 +126,8 @@ PHP callback. This `Event` object has getters for other contextual objects: If you would like to run the scripts for an event manually, the syntax is: - $ composer run-script [--dev] [--no-dev] script +```sh +composer run-script [--dev] [--no-dev] script +``` For example `composer run-script post-install-cmd` will run any **post-install-cmd** scripts that have been defined. diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index c1c5e2d61..838b7611c 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -63,12 +63,14 @@ You can fix this by aliasing version 0.11 to 0.1: composer.json: - { - "require": { - "A": "0.2", - "B": "0.11 as 0.1" - } +```json +{ + "require": { + "A": "0.2", + "B": "0.11 as 0.1" } +} +``` See [aliases](aliases.md) for more information. @@ -76,7 +78,7 @@ See [aliases](aliases.md) for more information. If composer shows memory errors on some commands: - PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...> +`PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...>` The PHP `memory_limit` should be increased. @@ -86,17 +88,23 @@ The PHP `memory_limit` should be increased. To get the current `memory_limit` value, run: - php -r "echo ini_get('memory_limit').PHP_EOL;" +```sh +php -r "echo ini_get('memory_limit').PHP_EOL;" +``` Try increasing the limit in your `php.ini` file (ex. `/etc/php5/cli/php.ini` for Debian-like systems): - ; Use -1 for unlimited or define an explicit value like 512M - memory_limit = -1 +```ini +; Use -1 for unlimited or define an explicit value like 512M +memory_limit = -1 +``` Or, you can increase the limit with a command-line argument: - php -d memory_limit=-1 composer.phar <...> +```sh +php -d memory_limit=-1 composer.phar <...> +``` ## "The system cannot find the path specified" (Windows) @@ -123,18 +131,23 @@ Now Composer should install/update without asking for authentication. ## proc_open(): fork failed errors If composer shows proc_open() fork failed on some commands: - PHP Fatal error: Uncaught exception 'ErrorException' with message 'proc_open(): fork failed - Cannot allocate memory' in phar +`PHP Fatal error: Uncaught exception 'ErrorException' with message 'proc_open(): fork failed - Cannot allocate memory' in phar` This could be happening because the VPS runs out of memory and has no Swap space enabled. - [root@my_tiny_vps htdocs]# free -m - total used free shared buffers cached - Mem: 2048 357 1690 0 0 237 - -/+ buffers/cache: 119 1928 - Swap: 0 0 0 +```sh +free -m + +total used free shared buffers cached +Mem: 2048 357 1690 0 0 237 +-/+ buffers/cache: 119 1928 +Swap: 0 0 0 +``` To enable the swap you can use for example: - /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 - /sbin/mkswap /var/swap.1 - /sbin/swapon /var/swap.1 +```sh +/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 +/sbin/mkswap /var/swap.1 +/sbin/swapon /var/swap.1 +``` diff --git a/doc/articles/vendor-binaries.md b/doc/articles/vendor-binaries.md index b258dccb7..75087b5b8 100644 --- a/doc/articles/vendor-binaries.md +++ b/doc/articles/vendor-binaries.md @@ -20,10 +20,11 @@ It is defined by adding the `bin` key to a project's `composer.json`. It is specified as an array of files so multiple binaries can be added for any given project. - { - "bin": ["bin/my-script", "bin/my-other-script"] - } - +```json +{ + "bin": ["bin/my-script", "bin/my-other-script"] +} +``` ## What does defining a vendor binary in composer.json do? @@ -46,22 +47,26 @@ symlink is created from each dependency's binaries to `vendor/bin`. Say package `my-vendor/project-a` has binaries setup like this: - { - "name": "my-vendor/project-a", - "bin": ["bin/project-a-bin"] - } +```json +{ + "name": "my-vendor/project-a", + "bin": ["bin/project-a-bin"] +} +``` Running `composer install` for this `composer.json` will not do anything with `bin/project-a-bin`. Say project `my-vendor/project-b` has requirements setup like this: - { - "name": "my-vendor/project-b", - "require": { - "my-vendor/project-a": "*" - } +```json +{ + "name": "my-vendor/project-b", + "require": { + "my-vendor/project-a": "*" } +} +``` Running `composer install` for this `composer.json` will look at all of project-b's dependencies and install them to `vendor/bin`. @@ -95,11 +100,13 @@ Yes, there are two ways an alternate vendor binary location can be specified: An example of the former looks like this: - { - "config": { - "bin-dir": "scripts" - } +```json +{ + "config": { + "bin-dir": "scripts" } +} +``` Running `composer install` for this `composer.json` will result in all of the vendor binaries being installed in `scripts/` instead of diff --git a/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md b/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md index b5956ca19..bd38d1e40 100644 --- a/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md +++ b/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md @@ -11,13 +11,15 @@ This is common if your package is intended for a specific framework such as CakePHP, Drupal or WordPress. Here is an example composer.json file for a WordPress theme: - { - "name": "you/themename", - "type": "wordpress-theme", - "require": { - "composer/installers": "~1.0" - } +```json +{ + "name": "you/themename", + "type": "wordpress-theme", + "require": { + "composer/installers": "~1.0" } +} +``` Now when your theme is installed with Composer it will be placed into `wp-content/themes/themename/` folder. Check the @@ -30,13 +32,15 @@ useful example would be for a Drupal multisite setup where the package should be installed into your sites subdirectory. Here we are overriding the install path for a module that uses composer/installers: - { - "extra": { - "installer-paths": { - "sites/example.com/modules/{$name}": ["vendor/package"] - } +```json +{ + "extra": { + "installer-paths": { + "sites/example.com/modules/{$name}": ["vendor/package"] } } +} +``` Now the package would be installed to your folder location, rather than the default composer/installers determined location. From 7cec839d303e69e0d9395a680bb7933cc6ae19f2 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 20 May 2014 10:15:44 +0200 Subject: [PATCH 262/638] Fix the ZipDownloader to catch the exceptions thrown in Symfony process. The problem was introduced in 72d4bea8 and causes composer to not fallback to ZipArchive when unzip can not be executed (i.e. when proc_open() is not allowed). --- src/Composer/Downloader/ZipDownloader.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 71958948d..3097d06a8 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -39,11 +39,17 @@ class ZipDownloader extends ArchiveDownloader // try to use unzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { $command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path); - if (0 === $this->process->execute($command, $ignoredOutput)) { - return; - } + try { + if (0 === $this->process->execute($command, $ignoredOutput)) { + return; + } - $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); + } + catch(\Exception $e) + { + $processError = $e->getMessage(); + } } if (!class_exists('ZipArchive')) { From a115cfd0d8efbbc2b71a4e19101c6d0b64b6e15b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 22 May 2014 09:44:01 +0200 Subject: [PATCH 263/638] Fix regression in github fallback behavior --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 108c22f85..5e007b894 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -336,7 +336,7 @@ class RemoteFilesystem if (!$gitHubUtil->authorizeOAuth($this->originUrl) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message)) ) { - throw new TransportException('Could not authenticate against '.$this->originUrl); + throw new TransportException('Could not authenticate against '.$this->originUrl, 401); } } else { // 404s are only handled for github From f5f6e5eaac9fbcc56a8f324e1c109e509304f663 Mon Sep 17 00:00:00 2001 From: dmoreaulf Date: Fri, 23 May 2014 10:05:00 +0200 Subject: [PATCH 264/638] adding doc entry for composer/satis's PR 118 https://github.com/composer/satis/pull/118/commits --- doc/articles/handling-private-packages-with-satis.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 69da38196..4e6aa59b8 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -192,9 +192,12 @@ to your `satis.json`: ```json { - "require-dependencies": true + "require-dependencies": true, + "require-dev-dependencies": true } ``` When searching for packages, satis will attempt to resolve all the required packages from the listed repositories. Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`. + +Dev dependencies are packaged only if the `require-dev-dependencies` parameter is set to true. From e23665be591c6c5657b791a372c45136b2de3a7a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 23 May 2014 15:22:53 +0200 Subject: [PATCH 265/638] Fix replacers being picked if whatProvide was called before building the package whitelist, fixes #2991, fixes #2962 --- src/Composer/DependencyResolver/Pool.php | 1 + .../DependencyResolver/RuleSetGenerator.php | 2 +- src/Composer/DependencyResolver/Solver.php | 8 ++++--- src/Composer/Installer.php | 2 +- .../installer/broken-deps-do-not-replace.test | 17 +++++++++++++++ .../installer/replace-priorities.test | 7 +++++-- .../installer/replace-vendor-priorities.test | 21 ------------------- .../Fixtures/installer/suggest-replaced.test | 4 ++-- 8 files changed, 32 insertions(+), 30 deletions(-) delete mode 100644 tests/Composer/Test/Fixtures/installer/replace-vendor-priorities.test diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 853673905..baac24457 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -70,6 +70,7 @@ class Pool public function setWhitelist($whitelist) { $this->whitelist = $whitelist; + $this->providerCache = array(); } /** diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index f555f1205..5e571c73b 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -332,7 +332,7 @@ class RuleSetGenerator $this->rules = new RuleSet; $this->installedMap = $installedMap; - $this->whitelistedNames = array(); + $this->whitelistedMap = array(); foreach ($this->installedMap as $package) { $this->whitelistFromPackage($package); $this->whitelistFromUpdatePackages($package); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 3e101e0f3..6d6088729 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -127,7 +127,10 @@ class Solver foreach ($this->installed->getPackages() as $package) { $this->installedMap[$package->getId()] = $package; } + } + protected function checkForRootRequireProblems() + { foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'update': @@ -161,10 +164,9 @@ class Solver $this->jobs = $request->getJobs(); $this->setupInstalledMap(); - - $this->decisions = new Decisions($this->pool); - $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap); + $this->checkForRootRequireProblems(); + $this->decisions = new Decisions($this->pool); $this->watchGraph = new RuleWatchGraph; foreach ($this->rules as $rule) { diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 03e6beb92..86716e3d3 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -534,7 +534,7 @@ class Installer if ($reason instanceof Rule) { switch ($reason->getReason()) { case Rule::RULE_JOB_INSTALL: - $this->io->write(' REASON: Required by root: '.$reason->getRequiredPackage()); + $this->io->write(' REASON: Required by root: '.$reason->getPrettyString()); $this->io->write(''); break; case Rule::RULE_PACKAGE_REQUIRES: diff --git a/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test index c626db198..e2593ba35 100644 --- a/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test +++ b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test @@ -20,6 +20,23 @@ Broken dependencies should not lead to a replacer being installed which is not m } --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 + - c/c 1.0.0 requires x/x 1.0 -> no matching package found. + - b/b 1.0.0 requires c/c 1.* -> satisfiable by c/c[1.0.0]. + - Installation request for b/b 1.* -> satisfiable by b/b[1.0.0]. + +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 for more details. + +Read for further common problems. + --EXPECT-EXIT-CODE-- 2 --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/replace-priorities.test b/tests/Composer/Test/Fixtures/installer/replace-priorities.test index 2f27ba7b7..d69dd9a22 100644 --- a/tests/Composer/Test/Fixtures/installer/replace-priorities.test +++ b/tests/Composer/Test/Fixtures/installer/replace-priorities.test @@ -1,5 +1,5 @@ --TEST-- -Replace takes precedence only in higher priority repositories +Replace takes precedence only in higher priority repositories and if explicitly required --COMPOSER-- { "repositories": [ @@ -14,13 +14,15 @@ Replace takes precedence only in higher priority repositories "package": [ { "name": "package", "version": "1.0.0" }, { "name": "package2", "version": "1.0.0" }, + { "name": "package3", "version": "1.0.0", "require": { "forked": "*" } }, { "name": "hijacker", "version": "1.1.0", "replace": { "package": "1.1.0" } } ] } ], "require": { "package": "1.*", - "package2": "1.*" + "package2": "1.*", + "package3": "1.*" } } --RUN-- @@ -28,3 +30,4 @@ install --EXPECT-- Installing package (1.0.0) Installing forked (1.1.0) +Installing package3 (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/replace-vendor-priorities.test b/tests/Composer/Test/Fixtures/installer/replace-vendor-priorities.test deleted file mode 100644 index 86c491feb..000000000 --- a/tests/Composer/Test/Fixtures/installer/replace-vendor-priorities.test +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -Replacer of the same vendor takes precedence if same prio repo ---COMPOSER-- -{ - "repositories": [ - { - "type": "package", - "package": [ - { "name": "b/replacer", "version": "1.1.0", "replace": { "a/package": "1.1.0" } }, - { "name": "a/replacer", "version": "1.1.0", "replace": { "a/package": "1.1.0" } } - ] - } - ], - "require": { - "a/package": "1.*" - } -} ---RUN-- -install ---EXPECT-- -Installing a/replacer (1.1.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test index 0817c7e08..99d13a720 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test @@ -6,7 +6,7 @@ Suggestions are not displayed for packages if they are replaced { "type": "package", "package": [ - { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }, + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" }, "require": { "c/c": "*" } }, { "name": "c/c", "version": "1.0.0", "replace": { "b/b": "1.0.0" } } ] } @@ -25,5 +25,5 @@ install Generating autoload files --EXPECT-- -Installing a/a (1.0.0) Installing c/c (1.0.0) +Installing a/a (1.0.0) From e0886b94a2f2c2d2654999821c4444762af51e7e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 23 May 2014 17:09:16 +0200 Subject: [PATCH 266/638] Make Git::cleanEnv static --- src/Composer/Downloader/GitDownloader.php | 14 ++++---------- src/Composer/Package/Loader/RootPackageLoader.php | 3 +-- src/Composer/Package/Locker.php | 3 +-- src/Composer/Repository/Vcs/GitDriver.php | 3 +-- src/Composer/Util/Git.php | 2 +- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 2dc7532a9..2c4884320 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -28,7 +28,7 @@ class GitDownloader extends VcsDownloader */ public function doDownload(PackageInterface $package, $path, $url) { - $this->cleanEnv(); + GitUtil::cleanEnv(); $path = $this->normalizePath($path); $ref = $package->getSourceReference(); @@ -56,7 +56,7 @@ class GitDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $this->cleanEnv(); + GitUtil::cleanEnv(); $path = $this->normalizePath($path); if (!is_dir($path.'/.git')) { throw new \RuntimeException('The .git directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information'); @@ -90,7 +90,7 @@ class GitDownloader extends VcsDownloader */ public function getLocalChanges(PackageInterface $package, $path) { - $this->cleanEnv(); + GitUtil::cleanEnv(); $path = $this->normalizePath($path); if (!is_dir($path.'/.git')) { return; @@ -109,7 +109,7 @@ class GitDownloader extends VcsDownloader */ protected function cleanChanges(PackageInterface $package, $path, $update) { - $this->cleanEnv(); + GitUtil::cleanEnv(); $path = $this->normalizePath($path); if (!$changes = $this->getLocalChanges($package, $path)) { return; @@ -467,12 +467,6 @@ class GitDownloader extends VcsDownloader $this->hasStashedChanges = true; } - protected function cleanEnv() - { - $util = new GitUtil; - $util->cleanEnv(); - } - protected function normalizePath($path) { if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) { diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index f177f8eab..300adea17 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -191,8 +191,7 @@ class RootPackageLoader extends ArrayLoader private function guessGitVersion(array $config) { - $util = new GitUtil; - $util->cleanEnv(); + GitUtil::cleanEnv(); // try to fetch current version from git tags if (0 === $this->process->execute('git describe --exact-match --tags', $output)) { diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index ea5297cc3..aa2755915 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -327,8 +327,7 @@ class Locker $sourceRef = $package->getSourceReference() ?: $package->getDistReference(); switch ($sourceType) { case 'git': - $util = new GitUtil; - $util->cleanEnv(); + GitUtil::cleanEnv(); if (0 === $this->process->execute('git log -n1 --pretty=%ct '.escapeshellarg($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { $datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index a37633655..f8fbc715f 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -42,8 +42,7 @@ class GitDriver extends VcsDriver } else { $this->repoDir = $this->config->get('cache-vcs-dir') . '/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; - $util = new GitUtil; - $util->cleanEnv(); + GitUtil::cleanEnv(); $fs = new Filesystem(); $fs->ensureDirectoryExists(dirname($this->repoDir)); diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 4b6fcd6f2..f15922a3e 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -17,7 +17,7 @@ namespace Composer\Util; */ class Git { - public function cleanEnv() + public static function cleanEnv() { if (ini_get('safe_mode') && false === strpos(ini_get('safe_mode_allowed_env_vars'), 'GIT_ASKPASS')) { throw new \RuntimeException('safe_mode is enabled and safe_mode_allowed_env_vars does not contain GIT_ASKPASS, can not set env var. You can disable safe_mode with "-dsafe_mode=0" when running composer'); From 3ebc869060e67bad68eff43be944d240ddfdf21c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 23 May 2014 18:48:10 +0200 Subject: [PATCH 267/638] Extract functionality from GitDownloader to make it more reusable --- src/Composer/Downloader/GitDownloader.php | 149 +++------------------- src/Composer/Repository/Vcs/GitDriver.php | 44 +------ src/Composer/Util/Git.php | 136 ++++++++++++++++++++ 3 files changed, 159 insertions(+), 170 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 2c4884320..e33c9d790 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -15,6 +15,10 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; use Composer\Util\GitHub; use Composer\Util\Git as GitUtil; +use Composer\Util\ProcessExecutor; +use Composer\IO\IOInterface; +use Composer\Util\Filesystem; +use Composer\Config; /** * @author Jordi Boggiano @@ -22,6 +26,13 @@ use Composer\Util\Git as GitUtil; class GitDownloader extends VcsDownloader { private $hasStashedChanges = false; + private $gitUtil; + + public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null) + { + parent::__construct($io, $config, $process, $fs); + $this->gitUtil = new GitUtil($this->io, $this->config, $this->process, $this->filesystem); + } /** * {@inheritDoc} @@ -40,7 +51,7 @@ class GitDownloader extends VcsDownloader return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref)); }; - $this->runCommand($commandCallable, $url, $path, true); + $this->gitUtil->runCommand($commandCallable, $url, $path, true); $this->setPushUrl($path, $url); if ($newRef = $this->updateToCommit($path, $ref, $package->getPrettyVersion(), $package->getReleaseDate())) { @@ -66,17 +77,11 @@ class GitDownloader extends VcsDownloader $this->io->write(" Checking out ".$ref); $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; - // capture username/password from URL if there is one - $this->process->execute('git remote -v', $output, $path); - if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { - $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2])); - } - $commandCallable = function($url) use ($command) { return sprintf($command, escapeshellarg($url)); }; - $this->runCommand($commandCallable, $url, $path); + $this->gitUtil->runCommand($commandCallable, $url, $path); if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) { if ($target->getDistReference() === $target->getSourceReference()) { $target->setDistReference($newRef); @@ -273,7 +278,7 @@ class GitDownloader extends VcsDownloader if (empty($newReference)) { // no matching branch found, find the previous commit by date in all commits if (0 !== $this->process->execute(sprintf($guessTemplate, $date, '--all'), $output, $path)) { - throw new \RuntimeException('Failed to execute ' . $this->sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); + throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); } $newReference = trim($output); } @@ -287,135 +292,13 @@ class GitDownloader extends VcsDownloader } } - throw new \RuntimeException('Failed to execute ' . $this->sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); - } - - /** - * Runs a command doing attempts for each protocol supported by github. - * - * @param callable $commandCallable A callable building the command for the given url - * @param string $url - * @param string $cwd - * @param bool $initialClone If true, the directory if cleared between every attempt - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - protected function runCommand($commandCallable, $url, $cwd, $initialClone = false) - { - if ($initialClone) { - $origCwd = $cwd; - $cwd = null; - } - - if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) { - throw new \InvalidArgumentException('The source URL '.$url.' is invalid, ssh URLs should have a port number after ":".'."\n".'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); - } - - // public github, autoswitch protocols - if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/(.*)}', $url, $match)) { - $protocols = $this->config->get('github-protocols'); - if (!is_array($protocols)) { - throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols)); - } - $messages = array(); - foreach ($protocols as $protocol) { - if ('ssh' === $protocol) { - $url = "git@" . $match[1] . ":" . $match[2]; - } else { - $url = $protocol ."://" . $match[1] . "/" . $match[2]; - } - - if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput, $cwd)) { - return; - } - $messages[] = '- ' . $url . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); - if ($initialClone) { - $this->filesystem->removeDirectory($origCwd); - } - } - - // failed to checkout, first check git accessibility - $this->throwException('Failed to clone ' . $this->sanitizeUrl($url) .' via '.implode(', ', $protocols).' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); - } - - $command = call_user_func($commandCallable, $url); - if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { - // private github repository without git access, try https with auth - if (preg_match('{^git@'.$this->getGitHubDomainsRegex().':(.+?)\.git$}i', $url, $match)) { - if (!$this->io->hasAuthentication($match[1])) { - $gitHubUtil = new GitHub($this->io, $this->config, $this->process); - $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; - - if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { - $gitHubUtil->authorizeOAuthInteractively($match[1], $message); - } - } - - if ($this->io->hasAuthentication($match[1])) { - $auth = $this->io->getAuthentication($match[1]); - $url = 'https://'.rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@'.$match[1].'/'.$match[2].'.git'; - - $command = call_user_func($commandCallable, $url); - if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { - return; - } - } - } elseif ( // private non-github repo that failed to authenticate - $this->io->isInteractive() && - preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) && - strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false - ) { - // TODO this should use an auth manager class that prompts and stores in the config - if ($this->io->hasAuthentication($match[2])) { - $auth = $this->io->getAuthentication($match[2]); - } else { - $this->io->write($url.' requires Authentication'); - $auth = array( - 'username' => $this->io->ask('Username: '), - 'password' => $this->io->askAndHideAnswer('Password: '), - ); - } - - $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; - - $command = call_user_func($commandCallable, $url); - if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { - $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); - - return; - } - } - - if ($initialClone) { - $this->filesystem->removeDirectory($origCwd); - } - $this->throwException('Failed to execute ' . $this->sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); - } - } - - protected function getGitHubDomainsRegex() - { - return '('.implode('|', array_map('preg_quote', $this->config->get('github-domains'))).')'; - } - - protected function throwException($message, $url) - { - if (0 !== $this->process->execute('git --version', $ignoredOutput)) { - throw new \RuntimeException('Failed to clone '.$this->sanitizeUrl($url).', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); - } - - throw new \RuntimeException($message); - } - - protected function sanitizeUrl($message) - { - return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message); + throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); } protected function setPushUrl($path, $url) { // set push url for github projects - if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $url, $match)) { + if (preg_match('{^(?:https?|git)://'.GitUtil::getGitHubDomainsRegex($this->config).'/([^/]+)/([^/]+?)(?:\.git)?$}', $url, $match)) { $protocols = $this->config->get('github-protocols'); $pushUrl = 'git@'.$match[1].':'.$match[2].'/'.$match[3].'.git'; if ($protocols[0] !== 'git') { diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index f8fbc715f..422868442 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -56,7 +56,7 @@ class GitDriver extends VcsDriver } // update the repo if it is a valid git repository - if (is_dir($this->repoDir) && 0 === $this->process->execute('git remote', $output, $this->repoDir)) { + if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') { if (0 !== $this->process->execute('git remote update --prune origin', $output, $this->repoDir)) { $this->io->write('Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')'); } @@ -64,43 +64,13 @@ class GitDriver extends VcsDriver // clean up directory and do a fresh clone into it $fs->removeDirectory($this->repoDir); - $command = sprintf('git clone --mirror %s %s', escapeshellarg($this->url), escapeshellarg($this->repoDir)); - if (0 !== $this->process->execute($command, $output)) { - $output = $this->process->getErrorOutput(); + $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs); + $repoDir = $this->repoDir; + $commandCallable = function($url) use ($repoDir) { + return sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($repoDir)); + }; - if (0 !== $this->process->execute('git --version', $ignoredOutput)) { - throw new \RuntimeException('Failed to clone '.$this->url.', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); - } - - if ( - $this->io->isInteractive() && - preg_match('{(https?://)([^/]+)(.*)$}i', $this->url, $match) && - strpos($output, 'fatal: Authentication failed') !== false - ) { - if ($this->io->hasAuthentication($match[2])) { - $auth = $this->io->getAuthentication($match[2]); - } else { - $this->io->write($this->url.' requires Authentication'); - $auth = array( - 'username' => $this->io->ask('Username: '), - 'password' => $this->io->askAndHideAnswer('Password: '), - ); - } - - $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; - - $command = sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($this->repoDir)); - - if (0 === $this->process->execute($command, $output)) { - $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); - } else { - $output = $this->process->getErrorOutput(); - throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output); - } - } else { - throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output); - } - } + $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true); } } diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index f15922a3e..0f61a53be 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -12,11 +12,128 @@ namespace Composer\Util; +use Composer\Config; +use Composer\IO\IOInterface; + /** * @author Jordi Boggiano */ class Git { + protected $io; + protected $config; + protected $process; + protected $filesystem; + + public function __construct(IOInterface $io, Config $config, ProcessExecutor $process, Filesystem $fs) + { + $this->io = $io; + $this->config = $config; + $this->process = $process; + $this->filesystem = $fs; + } + + public function runCommand($commandCallable, $url, $cwd, $initialClone = false) + { + if ($initialClone) { + $origCwd = $cwd; + $cwd = null; + } + + if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) { + throw new \InvalidArgumentException('The source URL '.$url.' is invalid, ssh URLs should have a port number after ":".'."\n".'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); + } + + if (!$initialClone) { + // capture username/password from URL if there is one + $this->process->execute('git remote -v', $output, $path); + if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { + $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2])); + } + } + + // public github, autoswitch protocols + if (preg_match('{^(?:https?|git)://'.self::getGitHubDomainsRegex($this->config).'/(.*)}', $url, $match)) { + $protocols = $this->config->get('github-protocols'); + if (!is_array($protocols)) { + throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols)); + } + $messages = array(); + foreach ($protocols as $protocol) { + if ('ssh' === $protocol) { + $url = "git@" . $match[1] . ":" . $match[2]; + } else { + $url = $protocol ."://" . $match[1] . "/" . $match[2]; + } + + if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput, $cwd)) { + return; + } + $messages[] = '- ' . $url . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); + if ($initialClone) { + $this->filesystem->removeDirectory($origCwd); + } + } + + // failed to checkout, first check git accessibility + $this->throwException('Failed to clone ' . self::sanitizeUrl($url) .' via '.implode(', ', $protocols).' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); + } + + $command = call_user_func($commandCallable, $url); + if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { + // private github repository without git access, try https with auth + if (preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url, $match)) { + if (!$this->io->hasAuthentication($match[1])) { + $gitHubUtil = new GitHub($this->io, $this->config, $this->process); + $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; + + if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { + $gitHubUtil->authorizeOAuthInteractively($match[1], $message); + } + } + + if ($this->io->hasAuthentication($match[1])) { + $auth = $this->io->getAuthentication($match[1]); + $url = 'https://'.rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@'.$match[1].'/'.$match[2].'.git'; + + $command = call_user_func($commandCallable, $url); + if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { + return; + } + } + } elseif ( // private non-github repo that failed to authenticate + $this->io->isInteractive() && + preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) && + strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false + ) { + // TODO this should use an auth manager class that prompts and stores in the config + if ($this->io->hasAuthentication($match[2])) { + $auth = $this->io->getAuthentication($match[2]); + } else { + $this->io->write($url.' requires Authentication'); + $auth = array( + 'username' => $this->io->ask('Username: '), + 'password' => $this->io->askAndHideAnswer('Password: '), + ); + } + + $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; + + $command = call_user_func($commandCallable, $url); + if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { + $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); + + return; + } + } + + if ($initialClone) { + $this->filesystem->removeDirectory($origCwd); + } + $this->throwException('Failed to execute ' . self::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); + } + } + public static function cleanEnv() { if (ini_get('safe_mode') && false === strpos(ini_get('safe_mode_allowed_env_vars'), 'GIT_ASKPASS')) { @@ -36,4 +153,23 @@ class Git putenv('GIT_WORK_TREE'); } } + + public static function getGitHubDomainsRegex(Config $config) + { + return '('.implode('|', array_map('preg_quote', $config->get('github-domains'))).')'; + } + + public static function sanitizeUrl($message) + { + return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message); + } + + private function throwException($message, $url) + { + if (0 !== $this->process->execute('git --version', $ignoredOutput)) { + throw new \RuntimeException('Failed to clone '.self::sanitizeUrl($url).', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()); + } + + throw new \RuntimeException($message); + } } From 1ce419cc43bcd6fc8d0a9294f5ce9efb8104ece6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 23 May 2014 18:55:44 +0200 Subject: [PATCH 268/638] Fix var name --- src/Composer/Util/Git.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 0f61a53be..296aa548f 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -46,7 +46,7 @@ class Git if (!$initialClone) { // capture username/password from URL if there is one - $this->process->execute('git remote -v', $output, $path); + $this->process->execute('git remote -v', $output, $cwd); if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2])); } From 1d15910fa6350f3d9509e7e9d72672880c90657a Mon Sep 17 00:00:00 2001 From: Stephan Hochdoerfer Date: Mon, 26 May 2014 13:02:34 +0200 Subject: [PATCH 269/638] Will read configured http basic auth credentials from users auth.json file and pass the credentials to the configured IOInterface. --- src/Composer/Factory.php | 63 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index b1bdd9599..3ab683780 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -36,14 +36,11 @@ use Composer\Package\Version\VersionParser; class Factory { /** - * @throws \RuntimeException - * @return Config + * @return string */ - public static function createConfig() + protected static function getHomeDir() { - // determine home and cache dirs $home = getenv('COMPOSER_HOME'); - $cacheDir = getenv('COMPOSER_CACHE_DIR'); if (!$home) { if (defined('PHP_WINDOWS_VERSION_MAJOR')) { if (!getenv('APPDATA')) { @@ -57,6 +54,16 @@ class Factory $home = rtrim(getenv('HOME'), '/') . '/.composer'; } } + + return $home; + } + + /** + * @return string + */ + protected static function getCacheDir($home) + { + $cacheDir = getenv('COMPOSER_CACHE_DIR'); if (!$cacheDir) { if (defined('PHP_WINDOWS_VERSION_MAJOR')) { if ($cacheDir = getenv('LOCALAPPDATA')) { @@ -70,6 +77,18 @@ class Factory } } + return $cacheDir; + } + + /** + * @return Config + */ + public static function createConfig() + { + // determine home and cache dirs + $home = self::getHomeDir(); + $cacheDir = self::getCacheDir($home); + // Protect directory against web access. Since HOME could be // the www-data's user home and be web-accessible it is a // potential security risk @@ -128,6 +147,26 @@ class Factory return $config; } + /** + * @return Config + */ + protected static function createAuthConfig() + { + $home = self::getHomeDir(); + + $config = new Config(); + // add dirs to the config + $config->merge(array('config' => array('home' => $home))); + + $file = new JsonFile($home.'/auth.json'); + if ($file->exists()) { + $config->merge($file->read()); + } + $config->setConfigSource(new JsonConfigSource($file)); + + return $config; + } + public static function getComposerFile() { return trim(getenv('COMPOSER')) ?: './composer.json'; @@ -214,6 +253,20 @@ class Factory $config->merge($localConfig); $io->loadConfiguration($config); + // load separate auth config + $authConfig = static::createAuthConfig(); + if ($basicauth = $authConfig->get('basic-auth')) { + foreach ($basicauth as $domain => $credentials) { + if(!isset($credentials['username'])) { + continue; + } + if(!isset($credentials['password'])) { + $credentials['password'] = null; + } + $io->setAuthentication($domain, $credentials['username'], $credentials['password']); + } + } + $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); From 90d1b6e08a3135db3edef44e12478ee34f33f933 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 13:50:47 +0200 Subject: [PATCH 270/638] Rename basic-auth to http-basic, add docs/schema/config support, add local auth file support, add storage to auth.json, add store-auths config option, refs #1862 --- doc/04-schema.md | 11 ++++ res/composer-schema.json | 11 +++- src/Composer/Command/ConfigCommand.php | 50 +++++++++++++++--- src/Composer/Config.php | 17 +++++- src/Composer/Config/ConfigSourceInterface.php | 7 +++ src/Composer/Config/JsonConfigSource.php | 52 +++++++++++++++++-- src/Composer/Factory.php | 51 ++++++------------ src/Composer/IO/BaseIO.php | 7 +++ src/Composer/Util/GitHub.php | 7 ++- src/Composer/Util/RemoteFilesystem.php | 37 ++++++++++++- .../Test/Repository/Vcs/GitHubDriverTest.php | 2 + 11 files changed, 202 insertions(+), 50 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 0ce4d4fdb..8ff2db7f2 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -743,6 +743,9 @@ The following options are supported: * **preferred-install:** Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option allows you to set the install method Composer will prefer to use. +* **store-auths:** What to do after prompting for authentication, one of: + `true` (always store), `false` (do not store) and `"prompt"` (ask every + time), defaults to `"prompt"`. * **github-protocols:** Defaults to `["git", "https", "ssh"]`. A list of protocols to use when cloning from github.com, in priority order. You can reconfigure it to for example prioritize the https protocol if you are behind a proxy or have somehow @@ -753,6 +756,9 @@ The following options are supported: rate limiting of their API. [Read more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) on how to get an OAuth token for GitHub. +* **http-basic:** A list of domain names and username/passwords to authenticate + against them. For example using + `{"example.org": {"username": "alice", "password": "foo"}` as the value of this option will let composer authenticate against example.org. * **vendor-dir:** Defaults to `vendor`. You can install dependencies into a different directory if you want to. * **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they @@ -802,6 +808,11 @@ Example: } ``` +> **Note:** Authentication-related config options like `http-basic` and +> `github-oauth` can also be specified inside a `auth.json` file that goes +> besides your `composer.json`. That way you can gitignore it and every +> developer can place their own credentials in there. + ### scripts (root-only) Composer allows you to hook into various parts of the installation process diff --git a/res/composer-schema.json b/res/composer-schema.json index 69d4dc5a1..f41f2fb0b 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -136,6 +136,15 @@ "description": "A hash of domain name => github API oauth tokens, typically {\"github.com\":\"\"}.", "additionalProperties": true }, + "http-basic": { + "type": "object", + "description": "A hash of domain name => {\"username\": \"...\", \"password\": \"...\"}.", + "additionalProperties": true + }, + "store-auths": { + "type": ["string", "boolean"], + "description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt." + }, "vendor-dir": { "type": "string", "description": "The location where all packages are installed, defaults to \"vendor\"." @@ -182,7 +191,7 @@ }, "optimize-autoloader": { "type": "boolean", - "description": "Always optimize when dumping the autoloader" + "description": "Always optimize when dumping the autoloader." }, "prepend-autoloader": { "type": "boolean", diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 48647512f..214f0341f 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -53,6 +53,7 @@ class ConfigCommand extends Command ->setDefinition(array( new InputOption('global', 'g', InputOption::VALUE_NONE, 'Apply command to the global config file'), new InputOption('editor', 'e', InputOption::VALUE_NONE, 'Open editor'), + new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'), new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'), new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'), new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json', 'composer.json'), @@ -113,12 +114,24 @@ EOT $this->configFile = new JsonFile($configFile); $this->configSource = new JsonConfigSource($this->configFile); + $authConfigFile = $input->getOption('global') + ? ($this->config->get('home') . '/auth.json') + : dirname(realpath($input->getOption('file'))) . '/auth.json'; + + $this->authConfigFile = new JsonFile($authConfigFile); + $this->authConfigSource = new JsonConfigSource($this->authConfigFile, true); + // initialize the global file if it's not there if ($input->getOption('global') && !$this->configFile->exists()) { touch($this->configFile->getPath()); $this->configFile->write(array('config' => new \ArrayObject)); @chmod($this->configFile->getPath(), 0600); } + if ($input->getOption('global') && !$this->authConfigFile->exists()) { + touch($this->authConfigFile->getPath()); + $this->authConfigFile->write(array('http-basic' => new \ArrayObject, 'github-oauth' => new \ArrayObject)); + @chmod($this->authConfigFile->getPath(), 0600); + } if (!$this->configFile->exists()) { throw new \RuntimeException('No composer.json found in the current directory'); @@ -146,13 +159,15 @@ EOT } } - system($editor . ' ' . $this->configFile->getPath() . (defined('PHP_WINDOWS_VERSION_BUILD') ? '': ' > `tty`')); + $file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath(); + system($editor . ' ' . $file . (defined('PHP_WINDOWS_VERSION_BUILD') ? '': ' > `tty`')); return 0; } if (!$input->getOption('global')) { $this->config->merge($this->configFile->read()); + $this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array())); } // List the configuration of the file settings @@ -236,16 +251,29 @@ EOT } // handle github-oauth - if (preg_match('/^github-oauth\.(.+)/', $settingKey, $matches)) { + if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { - return $this->configSource->removeConfigSetting('github-oauth.'.$matches[1]); + $this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]); + $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); + + return; } - if (1 !== count($values)) { - throw new \RuntimeException('Too many arguments, expected only one token'); + if ($matches[1] === 'github-oauth') { + if (1 !== count($values)) { + throw new \RuntimeException('Too many arguments, expected only one token'); + } + $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); + $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]); + } elseif ($matches[1] === 'http-basic') { + if (2 !== count($values)) { + throw new \RuntimeException('Expected two arguments (username, password), got '.count($values)); + } + $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); + $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1])); } - return $this->configSource->addConfigSetting('github-oauth.'.$matches[1], $values[0]); + return; } $booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); }; @@ -259,6 +287,16 @@ EOT function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); }, function ($val) { return $val; } ), + 'store-auths' => array( + function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); }, + function ($val) { + if ('prompt' === $val) { + return 'prompt'; + } + + return $val !== 'false' && (bool) $val; + } + ), 'notify-on-install' => array($booleanValidator, $booleanNormalizer), 'vendor-dir' => array('is_string', function ($val) { return $val; }), 'bin-dir' => array('is_string', function ($val) { return $val; }), diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 89f535725..cfc042465 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -39,6 +39,10 @@ class Config 'optimize-autoloader' => false, 'prepend-autoloader' => true, 'github-domains' => array('github.com'), + 'store-auths' => 'prompt', + // valid keys without defaults (auth config stuff): + // github-oauth + // http-basic ); public static $defaultRepositories = array( @@ -52,6 +56,7 @@ class Config private $config; private $repositories; private $configSource; + private $authConfigSource; public function __construct() { @@ -70,6 +75,16 @@ class Config return $this->configSource; } + public function setAuthConfigSource(ConfigSourceInterface $source) + { + $this->authConfigSource = $source; + } + + public function getAuthConfigSource() + { + return $this->authConfigSource; + } + /** * Merges new config values with the existing ones (overriding) * @@ -80,7 +95,7 @@ class Config // override defaults with given config if (!empty($config['config']) && is_array($config['config'])) { foreach ($config['config'] as $key => $val) { - if (in_array($key, array('github-oauth')) && isset($this->config[$key])) { + if (in_array($key, array('github-oauth', 'http-basic')) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); } else { $this->config[$key] = $val; diff --git a/src/Composer/Config/ConfigSourceInterface.php b/src/Composer/Config/ConfigSourceInterface.php index e1478dbbb..edd3dff8a 100644 --- a/src/Composer/Config/ConfigSourceInterface.php +++ b/src/Composer/Config/ConfigSourceInterface.php @@ -66,4 +66,11 @@ interface ConfigSourceInterface * @param string $name Name */ public function removeLink($type, $name); + + /** + * Gives a user-friendly name to this source (file path or so) + * + * @return string + */ + public function getName(); } diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 1c12aadcf..a4d97e344 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -28,14 +28,28 @@ class JsonConfigSource implements ConfigSourceInterface */ private $file; + /** + * @var bool + */ + private $authConfig; + /** * Constructor * * @param JsonFile $file */ - public function __construct(JsonFile $file) + public function __construct(JsonFile $file, $authConfig = false) { $this->file = $file; + $this->authConfig = $authConfig; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->file->getPath(); } /** @@ -64,7 +78,16 @@ class JsonConfigSource implements ConfigSourceInterface public function addConfigSetting($name, $value) { $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) { - $config['config'][$key] = $val; + if ($key === 'github-oauth' || $key === 'http-basic') { + list($key, $host) = explode('.', $key, 2); + if ($this->authConfig) { + $config[$key][$host] = $val; + } else { + $config['config'][$key][$host] = $val; + } + } else { + $config['config'][$key] = $val; + } }); } @@ -74,7 +97,16 @@ class JsonConfigSource implements ConfigSourceInterface public function removeConfigSetting($name) { $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) { - unset($config['config'][$key]); + if ($key === 'github-oauth' || $key === 'http-basic') { + list($key, $host) = explode('.', $key, 2); + if ($this->authConfig) { + unset($config[$key][$host]); + } else { + unset($config['config'][$key][$host]); + } + } else { + unset($config['config'][$key]); + } }); } @@ -107,13 +139,27 @@ class JsonConfigSource implements ConfigSourceInterface if ($this->file->exists()) { $contents = file_get_contents($this->file->getPath()); + } elseif ($this->authConfig) { + $contents = "{\n}\n"; } else { $contents = "{\n \"config\": {\n }\n}\n"; } + $manipulator = new JsonManipulator($contents); $newFile = !$this->file->exists(); + // override manipulator method for auth config files + if ($this->authConfig && $method === 'addConfigSetting') { + $method = 'addSubNode'; + list($mainNode, $name) = explode('.', $args[0], 2); + $args = array($mainNode, $name, $args[1]); + } elseif ($this->authConfig && $method === 'removeConfigSetting') { + $method = 'removeSubNode'; + list($mainNode, $name) = explode('.', $args[0], 2); + $args = array($mainNode, $name); + } + // try to update cleanly if (call_user_func_array(array($manipulator, $method), $args)) { file_put_contents($this->file->getPath(), $manipulator->getContents()); diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 3ab683780..6bfdaf98b 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -106,12 +106,20 @@ class Factory // add dirs to the config $config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir))); + // load global config $file = new JsonFile($home.'/config.json'); if ($file->exists()) { $config->merge($file->read()); } $config->setConfigSource(new JsonConfigSource($file)); + // load global auth file + $file = new JsonFile($config->get('home').'/auth.json'); + if ($file->exists()) { + $config->merge(array('config' => $file->read())); + } + $config->setAuthConfigSource(new JsonConfigSource($file, true)); + // move old cache dirs to the new locations $legacyPaths = array( 'cache-repo-dir' => array('/cache' => '/http*', '/cache.svn' => '/*', '/cache.github' => '/*'), @@ -147,26 +155,6 @@ class Factory return $config; } - /** - * @return Config - */ - protected static function createAuthConfig() - { - $home = self::getHomeDir(); - - $config = new Config(); - // add dirs to the config - $config->merge(array('config' => array('home' => $home))); - - $file = new JsonFile($home.'/auth.json'); - if ($file->exists()) { - $config->merge($file->read()); - } - $config->setConfigSource(new JsonConfigSource($file)); - - return $config; - } - public static function getComposerFile() { return trim(getenv('COMPOSER')) ?: './composer.json'; @@ -248,25 +236,20 @@ class Factory $localConfig = $file->read(); } - // Configuration defaults + // Load config and override with local config/auth config $config = static::createConfig(); $config->merge($localConfig); - $io->loadConfiguration($config); - - // load separate auth config - $authConfig = static::createAuthConfig(); - if ($basicauth = $authConfig->get('basic-auth')) { - foreach ($basicauth as $domain => $credentials) { - if(!isset($credentials['username'])) { - continue; - } - if(!isset($credentials['password'])) { - $credentials['password'] = null; - } - $io->setAuthentication($domain, $credentials['username'], $credentials['password']); + if (isset($composerFile)) { + $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); + if ($localAuthFile->exists()) { + $config->merge(array('config' => $localAuthFile->read())); + $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } } + // load auth configs into the IO instance + $io->loadConfiguration($config); + $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); diff --git a/src/Composer/IO/BaseIO.php b/src/Composer/IO/BaseIO.php index 29cae4f07..8d684833e 100644 --- a/src/Composer/IO/BaseIO.php +++ b/src/Composer/IO/BaseIO.php @@ -68,5 +68,12 @@ abstract class BaseIO implements IOInterface $this->setAuthentication($domain, $token, 'x-oauth-basic'); } } + + // reload http basic credentials from config if available + if ($creds = $config->get('http-basic')) { + foreach ($creds as $domain => $cred) { + $this->setAuthentication($domain, $cred['username'], $cred['password']); + } + } } } diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 69e3b46bf..a0b803db7 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -83,7 +83,7 @@ class GitHub if ($message) { $this->io->write($message); } - $this->io->write('The credentials will be swapped for an OAuth token stored in '.$this->config->get('home').'/config.json, your password will not be stored'); + $this->io->write('The credentials will be swapped for an OAuth token stored in '.$this->config->getAuthConfigSource()->getName().', your password will not be stored'); $this->io->write('To revoke access to this token you can visit https://github.com/settings/applications'); while ($attemptCounter++ < 5) { try { @@ -186,9 +186,8 @@ class GitHub $this->io->setAuthentication($originUrl, $contents['token'], 'x-oauth-basic'); // store value in user config - $githubTokens = $this->config->get('github-oauth') ?: array(); - $githubTokens[$originUrl] = $contents['token']; - $this->config->getConfigSource()->addConfigSetting('github-oauth', $githubTokens); + $this->config->getConfigSource()->removeConfigSetting('github-oauth.'.$originUrl); + $this->config->getAuthConfigSource()->addConfigSetting('github-oauth.'.$originUrl, $contents['token']); return true; } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 5e007b894..0eed1b223 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -37,6 +37,7 @@ class RemoteFilesystem private $options; private $retryAuthFailure; private $lastHeaders; + private $storeAuth; /** * Constructor. @@ -249,7 +250,40 @@ class RemoteFilesystem if ($this->retry) { $this->retry = false; - return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); + $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); + + $store = false; + $configSource = $this->config->getAuthConfigSource(); + if ($this->storeAuth === true) { + $store = $configSource; + } elseif ($this->storeAuth === 'prompt') { + $answer = $this->io->askAndValidate( + 'Do you want to store credentials for '.$this->originUrl.' in '.$configSource->getName().' ? [Yn] ', + function ($value) { + $input = strtolower(substr(trim($value), 0, 1)); + if (in_array($input, array('y','n'))) { + return $input; + } + throw new \RuntimeException('Please answer (y)es or (n)o'); + }, + false, + 'y' + ); + + if ($answer === 'y') { + $store = $configSource; + } + } + if ($store) { + $store->addConfigSetting( + 'http-basic.'.$this->originUrl, + $this->io->getAuthentication($this->originUrl) + ); + } + + $this->storeAuth = false; + + return $result; } if (false === $result) { @@ -364,6 +398,7 @@ class RemoteFilesystem $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); $this->io->setAuthentication($this->originUrl, $username, $password); + $this->storeAuth = $this->config->get('store-auths'); } $this->retry = true; diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index f452f224d..c0d55eeca 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -94,7 +94,9 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('{"master_branch": "test_master", "private": true}')); $configSource = $this->getMock('Composer\Config\ConfigSourceInterface'); + $authConfigSource = $this->getMock('Composer\Config\ConfigSourceInterface'); $this->config->setConfigSource($configSource); + $this->config->setAuthConfigSource($authConfigSource); $repoConfig = array( 'url' => $repoUrl, From af6ef235e11de5f283667448b2167e8d69af2f83 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 13:58:53 +0200 Subject: [PATCH 271/638] Update json schema --- composer.lock | 13 +++++++------ src/Composer/Json/JsonFile.php | 3 +-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 45ccf5710..f021ae47f 100644 --- a/composer.lock +++ b/composer.lock @@ -1,22 +1,23 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" ], "hash": "e68bf60f228ca192b8b492cb95a80fa7", "packages": [ { "name": "justinrainbow/json-schema", - "version": "1.3.5", + "version": "1.3.6", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "01949f6d2130e9737ffae5d3952909a8de70d114" + "reference": "d97cf3ce890fe80f247fc08594a1c8a1029fc7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/01949f6d2130e9737ffae5d3952909a8de70d114", - "reference": "01949f6d2130e9737ffae5d3952909a8de70d114", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/d97cf3ce890fe80f247fc08594a1c8a1029fc7ed", + "reference": "d97cf3ce890fe80f247fc08594a1c8a1029fc7ed", "shasum": "" }, "require": { @@ -71,7 +72,7 @@ "json", "schema" ], - "time": "2013-12-13 15:21:04" + "time": "2014-03-05 15:03:52" }, { "name": "seld/jsonlint", diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index abe79268f..999fa33e8 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -154,8 +154,7 @@ class JsonFile $schemaData = json_decode(file_get_contents($schemaFile)); if ($schema === self::LAX_SCHEMA) { - // TODO this should just be set to true, but this is a workaround for https://github.com/justinrainbow/json-schema/pull/94 - $schemaData->additionalProperties = (object) array('type' => array('object', 'string', 'array', 'number', 'null', 'boolean')); + $schemaData->additionalProperties = true; $schemaData->properties->name->required = false; $schemaData->properties->description->required = false; } From 534bd64cd15d8172d5176adf8737240a824bf394 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 13:59:32 +0200 Subject: [PATCH 272/638] Add support for manipulating empty json files --- src/Composer/Json/JsonManipulator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 3fd45012c..5547dbd4b 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -34,6 +34,9 @@ class JsonManipulator } $contents = trim($contents); + if ($contents === '') { + $contents = '{}'; + } if (!$this->pregMatch('#^\{(.*)\}$#s', $contents)) { throw new \InvalidArgumentException('The json file must be an object ({})'); } From c96430244c678b1cce3af8c739d88e977c9df268 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 14:11:20 +0200 Subject: [PATCH 273/638] Add missing setDistReference on alias package, fixes #3017 --- src/Composer/Package/AliasPackage.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index cf4da14ba..183f2c740 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -245,6 +245,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getDistReference(); } + public function setDistReference($reference) + { + return $this->aliasOf->setDistReference($reference); + } public function getDistSha1Checksum() { return $this->aliasOf->getDistSha1Checksum(); From 2ae084361667ec1b11f70c08826437ed223259e8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 14:19:37 +0200 Subject: [PATCH 274/638] Skip updates to same reference-locked version, fixes #2487, fixes #1333 --- src/Composer/Installer.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 86716e3d3..29a89a4ba 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -495,11 +495,6 @@ class Installer } } - $event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType()); - if (defined($event) && $this->runScripts) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation); - } - // not installing from lock, force dev packages' references if they're in root package refs if (!$installFromLock) { $package = null; @@ -515,6 +510,23 @@ class Installer $package->setDistReference($references[$package->getName()]); } } + if ('update' === $operation->getJobType() + && $operation->getTargetPackage()->isDev() + && $operation->getTargetPackage()->getVersion() === $operation->getInitialPackage()->getVersion() + && $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference() + ) { + if ($this->io->isDebug()) { + $this->io->write(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version'); + $this->io->write(''); + } + + continue; + } + } + + $event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType()); + if (defined($event) && $this->runScripts) { + $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation); } // output non-alias ops in dry run, output alias ops in debug verbosity From 2ed0bfc1ba6dda1b1bd318cc0418a6d1ac720e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ty=C3=A1s=20Somfai?= Date: Sat, 18 Jan 2014 21:09:23 +0100 Subject: [PATCH 275/638] added tests for not updating packages referenced by specific commit hash --- .../update-installed-reference-dry-run.test | 30 +++++++++++++++++++ .../installer/update-installed-reference.test | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-installed-reference.test diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test b/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test new file mode 100644 index 000000000..3c9036be4 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test @@ -0,0 +1,30 @@ +--TEST-- +Updating a dev package forcing it's reference, using dry run, should not do anything if the referenced version is the installed one +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/a", "version": "dev-master", + "source": { "reference": "abc123", "url": "", "type": "git" } + } + ] + } + ], + "require": { + "a/a": "dev-master#def000" + } +} +--INSTALLED-- +[ + { + "name": "a/a", "version": "dev-master", + "source": { "reference": "def000", "url": "", "type": "git" }, + "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" } + } +] +--RUN-- +update --dry-run +--EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-reference.test b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test new file mode 100644 index 000000000..e6814ccfe --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test @@ -0,0 +1,30 @@ +--TEST-- +Updating a dev package forcing it's reference should not do anything if the referenced version is the installed one +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/a", "version": "dev-master", + "source": { "reference": "abc123", "url": "", "type": "git" } + } + ] + } + ], + "require": { + "a/a": "dev-master#def000" + } +} +--INSTALLED-- +[ + { + "name": "a/a", "version": "dev-master", + "source": { "reference": "def000", "url": "", "type": "git" }, + "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" } + } +] +--RUN-- +update +--EXPECT-- From 7131607ad1d251c790ce566119d647e008972aa5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 27 May 2014 16:26:24 +0200 Subject: [PATCH 276/638] Fix regression in handling github-oauth settings in auth.json --- src/Composer/Json/JsonManipulator.php | 4 ++-- .../Composer/Test/Json/JsonManipulatorTest.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 5547dbd4b..e7c3e1459 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -125,7 +125,7 @@ class JsonManipulator } $subName = null; - if (false !== strpos($name, '.')) { + if (in_array($mainNode, array('config', 'repositories')) && false !== strpos($name, '.')) { list($name, $subName) = explode('.', $name, 2); } @@ -203,7 +203,7 @@ class JsonManipulator } $subName = null; - if (false !== strpos($name, '.')) { + if (in_array($mainNode, array('config', 'repositories')) && false !== strpos($name, '.')) { list($name, $subName) = explode('.', $name, 2); } diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 31ef70f24..9dd8c653d 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -735,6 +735,24 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase ', $manipulator->getContents()); } + public function testAddRootSettingDoesNotBreakDots() + { + $manipulator = new JsonManipulator('{ + "github-oauth": { + "github.com": "foo" + } +}'); + + $this->assertTrue($manipulator->addSubNode('github-oauth', 'bar', 'baz')); + $this->assertEquals('{ + "github-oauth": { + "github.com": "foo", + "bar": "baz" + } +} +', $manipulator->getContents()); + } + public function testRemoveConfigSettingCanRemoveSubKeyInHash() { $manipulator = new JsonManipulator('{ From a21b0f82db18808012984f86524cfcd9f13d1ad3 Mon Sep 17 00:00:00 2001 From: Benjamin Grandfond Date: Fri, 30 May 2014 17:14:43 +0200 Subject: [PATCH 277/638] Allow SVN to connect with credentials provided with the auth.json file --- src/Composer/Downloader/SvnDownloader.php | 2 +- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- src/Composer/Util/Svn.php | 84 +++++++++++++++---- .../Test/Repository/Vcs/SvnDriverTest.php | 3 - tests/Composer/Test/Util/SvnTest.php | 25 +++++- 5 files changed, 91 insertions(+), 25 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 495979d40..689781f6c 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -84,7 +84,7 @@ class SvnDownloader extends VcsDownloader */ protected function execute($baseUrl, $command, $url, $cwd = null, $path = null) { - $util = new SvnUtil($baseUrl, $this->io); + $util = new SvnUtil($baseUrl, $this->io, $this->config); try { return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose()); } catch (\RuntimeException $e) { diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 506c176b8..fde529b16 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -306,7 +306,7 @@ class SvnDriver extends VcsDriver protected function execute($command, $url) { if (null === $this->util) { - $this->util = new SvnUtil($this->baseUrl, $this->io, $this->process); + $this->util = new SvnUtil($this->baseUrl, $this->io, $this->config, $this->process); } try { diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 9db0930e6..bad4202c8 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -12,6 +12,7 @@ namespace Composer\Util; +use Composer\Config; use Composer\IO\IOInterface; /** @@ -57,15 +58,21 @@ class Svn */ protected $qtyAuthTries = 0; + /** + * @var \Composer\Config + */ + protected $config; + /** * @param string $url * @param \Composer\IO\IOInterface $io * @param ProcessExecutor $process */ - public function __construct($url, IOInterface $io, ProcessExecutor $process = null) + public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null) { $this->url = $url; $this->io = $io; + $this->config = $config; $this->process = $process ?: new ProcessExecutor; } @@ -123,16 +130,12 @@ class Svn throw new \RuntimeException($output); } - // no auth supported for non interactive calls - if (!$this->io->isInteractive()) { - throw new \RuntimeException( - 'can not ask for authentication in non interactive mode ('.$output.')' - ); + if (!$this->hasAuth()) { + $this->doAuthDance(); } // try to authenticate if maximum quantity of tries not reached - if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES || !$this->hasAuth()) { - $this->doAuthDance(); + if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES) { // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); @@ -147,9 +150,17 @@ class Svn * Repositories requests credentials, let's put them in. * * @return \Composer\Util\Svn + * @throws \RuntimeException */ protected function doAuthDance() { + // cannot ask for credentials in non interactive mode + if (!$this->io->isInteractive()) { + throw new \RuntimeException( + 'can not ask for authentication in non interactive mode' + ); + } + $this->io->write("The Subversion server ({$this->url}) requested credentials:"); $this->hasAuth = true; @@ -248,6 +259,53 @@ class Svn return $this->hasAuth; } + if (false === $this->createAuthFromConfig()) { + $this->createAuthFromUrl(); + } + + return $this->hasAuth; + } + + /** + * Return the no-auth-cache switch. + * + * @return string + */ + protected function getAuthCache() + { + return $this->cacheCredentials ? '' : '--no-auth-cache '; + } + + /** + * Create the auth params from the configuration file. + * + * @return bool + */ + protected function createAuthFromConfig() + { + if (!$this->config->has('http-basic')) { + return $this->hasAuth = false; + } + + $authConfig = $this->config->get('http-basic'); + + if (array_key_exists($this->url, $authConfig)) { + $this->credentials['username'] = $authConfig[$this->url]['username']; + $this->credentials['password'] = $authConfig[$this->url]['password']; + + return $this->hasAuth = true; + } + + return $this->hasAuth = false; + } + + /** + * Create the auth params from the url + * + * @return bool + */ + protected function createAuthFromUrl() + { $uri = parse_url($this->url); if (empty($uri['user'])) { return $this->hasAuth = false; @@ -260,14 +318,4 @@ class Svn return $this->hasAuth = true; } - - /** - * Return the no-auth-cache switch. - * - * @return string - */ - protected function getAuthCache() - { - return $this->cacheCredentials ? '' : '--no-auth-cache '; - } } diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 2f698565f..2ef1baa18 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -23,9 +23,6 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase public function testWrongCredentialsInUrl() { $console = $this->getMock('Composer\IO\IOInterface'); - $console->expects($this->exactly(6)) - ->method('isInteractive') - ->will($this->returnValue(true)); $output = "svn: OPTIONS of 'http://corp.svn.local/repo':"; $output .= " authorization failed: Could not authenticate to server:"; diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php index fb938d72e..69fb1c804 100644 --- a/tests/Composer/Test/Util/SvnTest.php +++ b/tests/Composer/Test/Util/SvnTest.php @@ -1,6 +1,7 @@ setAccessible(true); @@ -41,7 +42,7 @@ class SvnTest extends \PHPUnit_Framework_TestCase { $url = 'http://svn.example.org'; - $svn = new Svn($url, new NullIO()); + $svn = new Svn($url, new NullIO(), new Config()); $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCommand'); $reflMethod->setAccessible(true); @@ -51,6 +52,26 @@ class SvnTest extends \PHPUnit_Framework_TestCase ); } + public function testCredentialsFromConfig() + { + $url = 'http://svn.apache.org'; + + $config = new Config(); + $config->merge(array( + 'config' => array( + 'http-basic' => array( + $url => array('username' => 'foo', 'password' => 'bar') + ) + ) + )); + + $svn = new Svn($url, new NullIO, $config); + $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCredentialString'); + $reflMethod->setAccessible(true); + + $this->assertEquals($this->getCmd(" --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); + } + private function getCmd($cmd) { if (defined('PHP_WINDOWS_VERSION_BUILD')) { From 493ebbaacbccf9b7d8a0008d291146ae4ac7c14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Hochd=C3=B6rfer?= Date: Fri, 30 May 2014 18:37:47 +0200 Subject: [PATCH 278/638] Fix to load the auth information before the root package gets installed. --- src/Composer/Command/CreateProjectCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 3cca384d2..8adc7e106 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -133,6 +133,9 @@ EOT { $oldCwd = getcwd(); + // we need to manually load the configuration to pass the auth credentials to the io interface! + $io->loadConfiguration($config); + if ($packageName !== null) { $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositoryUrl, $disablePlugins, $noScripts, $keepVcs, $noProgress); } else { From 959cc4d63c6780af97273f1042e4e48827ece201 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 31 May 2014 16:50:33 +0200 Subject: [PATCH 279/638] Add info about conf file loading to debug output --- src/Composer/Command/ConfigCommand.php | 2 +- src/Composer/Factory.php | 18 +++++++++++++++--- tests/Composer/Test/Mock/FactoryMock.php | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 214f0341f..67a2f70a8 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -103,7 +103,7 @@ EOT throw new \RuntimeException('--file and --global can not be combined'); } - $this->config = Factory::createConfig(); + $this->config = Factory::createConfig($this->getIO()); // Get the local composer.json, global config.json, or if the user // passed in a file to use diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 6bfdaf98b..099ad37ea 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -83,7 +83,7 @@ class Factory /** * @return Config */ - public static function createConfig() + public static function createConfig(IOInterface $io = null) { // determine home and cache dirs $home = self::getHomeDir(); @@ -109,6 +109,9 @@ class Factory // load global config $file = new JsonFile($home.'/config.json'); if ($file->exists()) { + if ($io && $io->isDebug()) { + $io->write('Loading config file ' . $file->getPath()); + } $config->merge($file->read()); } $config->setConfigSource(new JsonConfigSource($file)); @@ -116,6 +119,9 @@ class Factory // load global auth file $file = new JsonFile($config->get('home').'/auth.json'); if ($file->exists()) { + if ($io && $io->isDebug()) { + $io->write('Loading config file ' . $file->getPath()); + } $config->merge(array('config' => $file->read())); } $config->setAuthConfigSource(new JsonConfigSource($file, true)); @@ -173,7 +179,7 @@ class Factory $repos = array(); if (!$config) { - $config = static::createConfig(); + $config = static::createConfig($io); } if (!$rm) { if (!$io) { @@ -237,11 +243,17 @@ class Factory } // Load config and override with local config/auth config - $config = static::createConfig(); + $config = static::createConfig($io); $config->merge($localConfig); if (isset($composerFile)) { + if ($io && $io->isDebug()) { + $io->write('Loading config file ' . $composerFile); + } $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); if ($localAuthFile->exists()) { + if ($io && $io->isDebug()) { + $io->write('Loading config file ' . $localAuthFile->getPath()); + } $config->merge(array('config' => $localAuthFile->read())); $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php index 11b266590..75d2a23bb 100644 --- a/tests/Composer/Test/Mock/FactoryMock.php +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -21,7 +21,7 @@ use Composer\IO\IOInterface; class FactoryMock extends Factory { - public static function createConfig() + public static function createConfig(IOInterface $io = null) { $config = new Config(); From effacc1185858308a45056b1c11c135490064541 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 31 May 2014 21:36:09 +0200 Subject: [PATCH 280/638] Do not realpath relative local URLs, fixes #2916 --- src/Composer/Downloader/VcsDownloader.php | 6 ++++++ src/Composer/Repository/Vcs/GitDriver.php | 11 +++++++---- src/Composer/Repository/Vcs/HgDriver.php | 6 +++--- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- src/Composer/Repository/Vcs/VcsDriver.php | 19 +++---------------- src/Composer/Util/Filesystem.php | 11 +++++++++++ 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index bff9201f7..55a12954e 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -60,6 +60,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $urls = $package->getSourceUrls(); while ($url = array_shift($urls)) { try { + if (Filesystem::isLocalPath($url)) { + $url = realpath($url); + } $this->doDownload($package, $path, $url); break; } catch (\Exception $e) { @@ -107,6 +110,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $urls = $target->getSourceUrls(); while ($url = array_shift($urls)) { try { + if (Filesystem::isLocalPath($url)) { + $url = realpath($url); + } $this->doUpdate($initial, $target, $path, $url); break; } catch (\Exception $e) { diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 422868442..0e6286906 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -37,8 +37,9 @@ class GitDriver extends VcsDriver */ public function initialize() { - if (static::isLocalUrl($this->url)) { - $this->repoDir = str_replace('file://', '', $this->url); + if (Filesystem::isLocalPath($this->url)) { + $this->repoDir = $this->url; + $cacheUrl = realpath($this->url); } else { $this->repoDir = $this->config->get('cache-vcs-dir') . '/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; @@ -72,12 +73,14 @@ class GitDriver extends VcsDriver $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true); } + + $cacheUrl = $this->url; } $this->getTags(); $this->getBranches(); - $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url)); + $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl)); } /** @@ -215,7 +218,7 @@ class GitDriver extends VcsDriver } // local filesystem - if (static::isLocalUrl($url)) { + if (Filesystem::isLocalPath($url)) { if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: '.$url); } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 060a77e45..7b9d1ff8c 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -34,8 +34,8 @@ class HgDriver extends VcsDriver */ public function initialize() { - if (static::isLocalUrl($this->url)) { - $this->repoDir = str_replace('file://', '', $this->url); + if (Filesystem::isLocalPath($this->url)) { + $this->repoDir = $this->url; } else { $cacheDir = $this->config->get('cache-vcs-dir'); $this->repoDir = $cacheDir . '/' . preg_replace('{[^a-z0-9]}i', '-', $this->url) . '/'; @@ -197,7 +197,7 @@ class HgDriver extends VcsDriver } // local filesystem - if (static::isLocalUrl($url)) { + if (Filesystem::isLocalPath($url)) { if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: '.$url); } diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 506c176b8..4f8e07f20 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -252,7 +252,7 @@ class SvnDriver extends VcsDriver } // proceed with deep check for local urls since they are fast to process - if (!$deep && !static::isLocalUrl($url)) { + if (!$deep && !Filesystem::isLocalPath($url)) { return false; } diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 04956e816..262309e67 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -17,6 +17,7 @@ use Composer\Config; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; +use Composer\Util\Filesystem; /** * A driver implementation for driver with authentication interaction. @@ -44,11 +45,8 @@ abstract class VcsDriver implements VcsDriverInterface */ final public function __construct(array $repoConfig, IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) { - - if (self::isLocalUrl($repoConfig['url'])) { - $repoConfig['url'] = realpath( - preg_replace('/^file:\/\//', '', $repoConfig['url']) - ); + if (Filesystem::isLocalPath($repoConfig['url'])) { + $repoConfig['url'] = preg_replace('{^file://}', '', $repoConfig['url']); } $this->url = $repoConfig['url']; @@ -101,17 +99,6 @@ abstract class VcsDriver implements VcsDriverInterface return $this->remoteFilesystem->getContents($this->originUrl, $url, false); } - /** - * Return if current repository url is local - * - * @param string $url - * @return boolean Repository url is local - */ - protected static function isLocalUrl($url) - { - return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url); - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index d88e00393..e8755645a 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -357,6 +357,17 @@ class Filesystem return $prefix.($absolute ? '/' : '').implode('/', $parts); } + /** + * Return if the given path is local + * + * @param string $path + * @return bool + */ + public static function isLocalPath($path) + { + return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/]|\.\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i', $path); + } + protected function directorySize($directory) { $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); From 443858dae7632570b5b21d411b98d9cb51b069f6 Mon Sep 17 00:00:00 2001 From: Richard Quadling Date: Wed, 23 Apr 2014 18:07:12 +0100 Subject: [PATCH 281/638] Force all glob results to be realpath'd. --- src/Composer/Command/SelfUpdateCommand.php | 3 ++- src/Composer/Downloader/ArchiveDownloader.php | 2 +- src/Composer/Factory.php | 4 +++- src/Composer/Installer/LibraryInstaller.php | 2 +- src/Composer/Util/Filesystem.php | 19 ++++++++++++++++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 8ce355d9c..010f68984 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -213,6 +213,7 @@ EOT protected function getOldInstallationFiles($rollbackDir) { - return glob($rollbackDir . '/*' . self::OLD_INSTALL_EXT) ?: array(); + $fs = new Filesystem; + return $fs->realpathGlob($rollbackDir . '/*' . self::OLD_INSTALL_EXT) ?: array(); } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index bfe174fb7..a412dc830 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -132,7 +132,7 @@ abstract class ArchiveDownloader extends FileDownloader */ private function listFiles($dir) { - $files = array_merge(glob($dir . '/.*') ?: array(), glob($dir . '/*') ?: array()); + $files = array_merge($this->filesystem->realpathGlob($dir . '/.*') ?: array(), $this->filesystem->realpathGlob($dir . '/*') ?: array()); return array_values(array_filter($files, function ($el) { return basename($el) !== '.' && basename($el) !== '..'; diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 099ad37ea..5a4747b48 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -20,6 +20,7 @@ use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; +use Composer\Util\Filesystem; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Composer\EventDispatcher\EventDispatcher; use Composer\Autoload\AutoloadGenerator; @@ -132,6 +133,7 @@ class Factory 'cache-vcs-dir' => array('/cache.git' => '/*', '/cache.hg' => '/*'), 'cache-files-dir' => array('/cache.files' => '/*'), ); + $fs = new Filesystem; foreach ($legacyPaths as $key => $oldPaths) { foreach ($oldPaths as $oldPath => $match) { $dir = $config->get($key); @@ -146,7 +148,7 @@ class Factory continue; } } - if (is_array($children = glob($oldPathMatch))) { + if (is_array($children = $fs->realpathGlob($oldPathMatch))) { foreach ($children as $child) { @rename($child, $dir.'/'.basename($child)); } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 646f801d3..7fb0f394c 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -126,7 +126,7 @@ class LibraryInstaller implements InstallerInterface $downloadPath = $this->getPackageBasePath($package); if (strpos($package->getName(), '/')) { $packageVendorDir = dirname($downloadPath); - if (is_dir($packageVendorDir) && !glob($packageVendorDir.'/*')) { + if (is_dir($packageVendorDir) && !$this->filesystem->realpathGlob($packageVendorDir.'/*')) { @rmdir($packageVendorDir); } } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index e8755645a..3ef17452e 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -41,6 +41,23 @@ class Filesystem return false; } + /** + * Force the results of a glob to be realpaths. + * + * @param string $pattern + * @param int $flags + * @return array + */ + public function realpathGlob($pattern, $flags = 0) + { + $matches = glob($pattern, $flags); + if (!$matches) { + return false; + } + var_dump($matches); + return array_map('realpath', $matches); + } + /** * Checks if a directory is empty * @@ -51,7 +68,7 @@ class Filesystem { $dir = rtrim($dir, '/\\'); - return count(glob($dir.'/*') ?: array()) === 0 && count(glob($dir.'/.*') ?: array()) === 2; + return count($this->realpathGlob($dir.'/*') ?: array()) === 0 && count($this->realpathGlob($dir.'/.*') ?: array()) === 2; } /** From 56c5af8dc405b3fc107a4895c155e24f94c9e809 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 1 Jun 2014 15:15:20 +0200 Subject: [PATCH 282/638] realpathGlob tweaks, refs #2932 --- src/Composer/Command/SelfUpdateCommand.php | 2 +- src/Composer/Downloader/ArchiveDownloader.php | 2 +- src/Composer/Installer/LibraryInstaller.php | 2 +- src/Composer/Util/Filesystem.php | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 010f68984..e1cbdbbda 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -214,6 +214,6 @@ EOT protected function getOldInstallationFiles($rollbackDir) { $fs = new Filesystem; - return $fs->realpathGlob($rollbackDir . '/*' . self::OLD_INSTALL_EXT) ?: array(); + return $fs->realpathGlob($rollbackDir . '/*' . self::OLD_INSTALL_EXT); } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index a412dc830..1f37ec974 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -132,7 +132,7 @@ abstract class ArchiveDownloader extends FileDownloader */ private function listFiles($dir) { - $files = array_merge($this->filesystem->realpathGlob($dir . '/.*') ?: array(), $this->filesystem->realpathGlob($dir . '/*') ?: array()); + $files = array_merge($this->filesystem->realpathGlob($dir . '/.*'), $this->filesystem->realpathGlob($dir . '/*')); return array_values(array_filter($files, function ($el) { return basename($el) !== '.' && basename($el) !== '..'; diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 7fb0f394c..b847eac69 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -126,7 +126,7 @@ class LibraryInstaller implements InstallerInterface $downloadPath = $this->getPackageBasePath($package); if (strpos($package->getName(), '/')) { $packageVendorDir = dirname($downloadPath); - if (is_dir($packageVendorDir) && !$this->filesystem->realpathGlob($packageVendorDir.'/*')) { + if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) { @rmdir($packageVendorDir); } } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 3ef17452e..b45135423 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -52,9 +52,9 @@ class Filesystem { $matches = glob($pattern, $flags); if (!$matches) { - return false; + return array(); } - var_dump($matches); + return array_map('realpath', $matches); } From 9b580bd800729f6c71a6718ef3a0c06aa09b5dbc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 1 Jun 2014 15:38:46 +0200 Subject: [PATCH 283/638] Do not realpath ./.., refs #2932 --- src/Composer/Util/Filesystem.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index b45135423..e81cacdb7 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -55,7 +55,13 @@ class Filesystem return array(); } - return array_map('realpath', $matches); + return array_map(function ($path) { + if (basename($path) === '.' || basename($path) === '..') { + return $path; + } + + return realpath($path); + }, $matches); } /** From f16e3a88e28228af67cc98950a2b621713f4aac3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 3 Jun 2014 10:46:14 +0200 Subject: [PATCH 284/638] Clean up code format and error message --- src/Composer/Downloader/ZipDownloader.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 3097d06a8..88c0e4346 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -45,10 +45,8 @@ class ZipDownloader extends ArchiveDownloader } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); - } - catch(\Exception $e) - { - $processError = $e->getMessage(); + } catch(\Exception $e) { + $processError = 'Failed to execute ' . $command . "\n\n" . $e->getMessage(); } } From 31b95ed02c0c86fdd84168c257e0269d2e6a5e4e Mon Sep 17 00:00:00 2001 From: Benjamin Grandfond Date: Tue, 3 Jun 2014 13:34:58 +0200 Subject: [PATCH 285/638] Make auth credential creation private --- src/Composer/Util/Svn.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index bad4202c8..e8eb6fc78 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -281,7 +281,7 @@ class Svn * * @return bool */ - protected function createAuthFromConfig() + private function createAuthFromConfig() { if (!$this->config->has('http-basic')) { return $this->hasAuth = false; @@ -304,7 +304,7 @@ class Svn * * @return bool */ - protected function createAuthFromUrl() + private function createAuthFromUrl() { $uri = parse_url($this->url); if (empty($uri['user'])) { From 421c9453a4575fab28e719ed84e5b82da277c171 Mon Sep 17 00:00:00 2001 From: David Neilsen Date: Wed, 4 Jun 2014 19:32:28 +1200 Subject: [PATCH 286/638] Add clear cache command. --- src/Composer/Command/ClearCacheCommand.php | 65 ++++++++++++++++++++++ src/Composer/Console/Application.php | 1 + 2 files changed, 66 insertions(+) create mode 100644 src/Composer/Command/ClearCacheCommand.php diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php new file mode 100644 index 000000000..5ebfe8fa7 --- /dev/null +++ b/src/Composer/Command/ClearCacheCommand.php @@ -0,0 +1,65 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\Cache; +use Composer\Factory; +use Composer\Installer; +use Composer\Plugin\CommandEvent; +use Composer\Plugin\PluginEvents; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author David Neilsen + */ +class ClearCacheCommand extends Command +{ + protected function configure() + { + $this + ->setName('clear-cache') + ->setAliases(array('clearcache')) + ->setDescription('Clears composer\'s interal package cache.') + ->setHelp(<<clear-cache deletes all cached packages from composer's +cache directory. +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $config = Factory::createConfig(); + $io = $this->getIO(); + + $cachePath = realpath($config->get('cache-repo-dir')); + if (!$cachePath) { + $io->write('Cache directory does not exist.'); + return; + } + + $cache = new Cache($io, $cachePath); + if (!$cache->isEnabled()) { + $io->write('Cache is not enabled.'); + return; + } + + $io->write('Clearing cache in: '.$cachePath.''); + $cache->gc(0, 0); + $io->write('Cache cleared.'); + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index da5d4ff20..12da0c2da 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -227,6 +227,7 @@ class Application extends BaseApplication $commands[] = new Command\RunScriptCommand(); $commands[] = new Command\LicensesCommand(); $commands[] = new Command\GlobalCommand(); + $commands[] = new Command\ClearCacheCommand(); if ('phar:' === substr(__FILE__, 0, 5)) { $commands[] = new Command\SelfUpdateCommand(); From 075c85dd48a2806e4ab2c81880af07af8c620bbf Mon Sep 17 00:00:00 2001 From: David Neilsen Date: Wed, 4 Jun 2014 21:25:43 +1200 Subject: [PATCH 287/638] Fix typo in src/Composer/Command/ClearCacheCommand.php --- src/Composer/Command/ClearCacheCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index 5ebfe8fa7..ca229b662 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -32,7 +32,7 @@ class ClearCacheCommand extends Command $this ->setName('clear-cache') ->setAliases(array('clearcache')) - ->setDescription('Clears composer\'s interal package cache.') + ->setDescription('Clears composer\'s internal package cache.') ->setHelp(<<clear-cache deletes all cached packages from composer's cache directory. From 7fe342699198117dbe990dcf707af5d67bc13161 Mon Sep 17 00:00:00 2001 From: David Neilsen Date: Wed, 4 Jun 2014 21:28:41 +1200 Subject: [PATCH 288/638] Clean up unused 'use' statements --- src/Composer/Command/ClearCacheCommand.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index ca229b662..f49b00bc3 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -14,12 +14,7 @@ namespace Composer\Command; use Composer\Cache; use Composer\Factory; -use Composer\Installer; -use Composer\Plugin\CommandEvent; -use Composer\Plugin\PluginEvents; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; /** From 71397f82e4bdf4722bcbfeb43be0d30831376d3d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 4 Jun 2014 14:20:36 +0200 Subject: [PATCH 289/638] Remove forced dir removal before install, fixes #3035 --- src/Composer/Downloader/FileDownloader.php | 3 +-- src/Composer/Downloader/VcsDownloader.php | 2 +- src/Composer/Util/Filesystem.php | 20 +++++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 709835444..fc903fd66 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -107,8 +107,7 @@ class FileDownloader implements DownloaderInterface protected function doDownload(PackageInterface $package, $path, $url) { - $this->filesystem->removeDirectory($path); - $this->filesystem->ensureDirectoryExists($path); + $this->filesystem->emptyDirectory($path); $fileName = $this->getFileName($package, $path); diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 55a12954e..b0f0b7cbc 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -55,7 +55,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa } $this->io->write(" - Installing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")"); - $this->filesystem->removeDirectory($path); + $this->filesystem->emptyDirectory($path); $urls = $package->getSourceUrls(); while ($url = array_shift($urls)) { diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index e81cacdb7..fab4c5793 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -74,7 +74,25 @@ class Filesystem { $dir = rtrim($dir, '/\\'); - return count($this->realpathGlob($dir.'/*') ?: array()) === 0 && count($this->realpathGlob($dir.'/.*') ?: array()) === 2; + return count($this->realpathGlob($dir.'/*')) === 0 && count($this->realpathGlob($dir.'/.*')) === 2; + } + + public function emptyDirectory($dir, $ensureDirectoryExists = true) + { + if ($ensureDirectoryExists) { + $this->ensureDirectoryExists($dir); + } + + if (is_dir($dir)) { + foreach ($this->realpathGlob(rtrim($dir, '\\/').'/*') as $path) { + $this->remove($path); + } + foreach ($this->realpathGlob(rtrim($dir, '\\/').'/.*') as $path) { + if (basename($path) !== '..' && basename($path) !== '.') { + $this->remove($path); + } + } + } } /** From 44e25be06db358bd2c4151144d572263e149b9f4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 5 Jun 2014 09:39:51 +0200 Subject: [PATCH 290/638] Use inline html to work around parsedown bug, fixes composer/getcomposer.org#68 --- doc/01-basic-usage.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 270a6d50a..803032606 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -72,12 +72,12 @@ means any version in the `1.0` development branch. It would match `1.0.0`, Version constraints can be specified in a few different ways. -Name | Example | Description --------------- | --------------------- | ----------- -Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` `>=1.0,<1.1 | >=1.2` | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges, separated by a comma, which will be treated as a **logical AND**. A pipe symbol `|` will be treated as a **logical OR**.
AND has higher precedence than OR. -Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. -Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. +Name | Example | Description +-------------- | ------------------------------------------------------------------ | ----------- +Exact version | `1.0.2` | You can specify the exact version of a package. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges, separated by a comma, which will be treated as a **logical AND**. A pipe symbol | will be treated as a **logical OR**. AND has higher precedence than OR. +Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. +Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. ### Next Significant Release (Tilde Operator) From 08e34858d64740f74c5c5a6fa22b425ef12d6e3e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 5 Jun 2014 11:14:29 +0200 Subject: [PATCH 291/638] Fix code to use hostname only, refs #3026 --- src/Composer/Util/Svn.php | 7 ++++--- tests/Composer/Test/Util/SvnTest.php | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index e8eb6fc78..a2836075f 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -289,9 +289,10 @@ class Svn $authConfig = $this->config->get('http-basic'); - if (array_key_exists($this->url, $authConfig)) { - $this->credentials['username'] = $authConfig[$this->url]['username']; - $this->credentials['password'] = $authConfig[$this->url]['password']; + $host = parse_url($this->url, PHP_URL_HOST); + if (isset($authConfig[$host])) { + $this->credentials['username'] = $authConfig[$host]['username']; + $this->credentials['password'] = $authConfig[$host]['password']; return $this->hasAuth = true; } diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php index 69fb1c804..95bc6691e 100644 --- a/tests/Composer/Test/Util/SvnTest.php +++ b/tests/Composer/Test/Util/SvnTest.php @@ -60,7 +60,7 @@ class SvnTest extends \PHPUnit_Framework_TestCase $config->merge(array( 'config' => array( 'http-basic' => array( - $url => array('username' => 'foo', 'password' => 'bar') + 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar') ) ) )); From 8035dbd7147b1b97237a425e67a3513460108356 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 7 Jun 2014 17:04:06 +0200 Subject: [PATCH 292/638] Add full timezone date example, fixes #3038 --- res/composer-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index f41f2fb0b..9b1b31d42 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -39,7 +39,7 @@ }, "time": { "type": "string", - "description": "Package release date, in 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' format." + "description": "Package release date, in 'YYYY-MM-DD', 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DDTHH:MM:SSZ' format." }, "license": { "type": ["string", "array"], From 15a99f31b3aae3f3b688f10683c6cb943663d5b1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Jun 2014 13:11:25 +0200 Subject: [PATCH 293/638] Remove legacy cache handling --- src/Composer/Factory.php | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 5a4747b48..d65bc44a8 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -127,39 +127,6 @@ class Factory } $config->setAuthConfigSource(new JsonConfigSource($file, true)); - // move old cache dirs to the new locations - $legacyPaths = array( - 'cache-repo-dir' => array('/cache' => '/http*', '/cache.svn' => '/*', '/cache.github' => '/*'), - 'cache-vcs-dir' => array('/cache.git' => '/*', '/cache.hg' => '/*'), - 'cache-files-dir' => array('/cache.files' => '/*'), - ); - $fs = new Filesystem; - foreach ($legacyPaths as $key => $oldPaths) { - foreach ($oldPaths as $oldPath => $match) { - $dir = $config->get($key); - if ('/cache.github' === $oldPath) { - $dir .= '/github.com'; - } - $oldPath = $config->get('home').$oldPath; - $oldPathMatch = $oldPath . $match; - if (is_dir($oldPath) && $dir !== $oldPath) { - if (!is_dir($dir)) { - if (!@mkdir($dir, 0777, true)) { - continue; - } - } - if (is_array($children = $fs->realpathGlob($oldPathMatch))) { - foreach ($children as $child) { - @rename($child, $dir.'/'.basename($child)); - } - } - if ($config->get('cache-dir') != $oldPath) { - @rmdir($oldPath); - } - } - } - } - return $config; } From e890d1bc5900f06a00dcd19e3504b2e1edc0f33e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Jun 2014 13:12:42 +0200 Subject: [PATCH 294/638] Remove use of glob, fixes #3042 --- src/Composer/Command/SelfUpdateCommand.php | 39 ++++++++------- src/Composer/Downloader/ArchiveDownloader.php | 16 ++++--- src/Composer/Util/Filesystem.php | 47 ++++++------------- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index e1cbdbbda..cad41df51 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -21,6 +21,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Finder\Finder; /** * @author Igor Wiedler @@ -113,15 +114,13 @@ EOT // remove saved installations of composer if ($input->getOption('clean-backups')) { - $files = $this->getOldInstallationFiles($rollbackDir); + $finder = $this->getOldInstallationFinder($rollbackDir); - if (!empty($files)) { - $fs = new Filesystem; - - foreach ($files as $file) { - $output->writeln('Removing: '.$file.''); - $fs->remove($file); - } + $fs = new Filesystem; + foreach ($finder as $file) { + $file = (string) $file; + $output->writeln('Removing: '.$file.''); + $fs->remove($file); } } @@ -201,19 +200,25 @@ EOT protected function getLastBackupVersion($rollbackDir) { - $files = $this->getOldInstallationFiles($rollbackDir); - if (empty($files)) { - return false; + $finder = $this->getOldInstallationFinder($rollbackDir); + $finder->sortByName(); + $files = iterator_to_array($finder); + + if (count($files)) { + return basename(end($files), self::OLD_INSTALL_EXT); } - sort($files); - - return basename(end($files), self::OLD_INSTALL_EXT); + return false; } - protected function getOldInstallationFiles($rollbackDir) + protected function getOldInstallationFinder($rollbackDir) { - $fs = new Filesystem; - return $fs->realpathGlob($rollbackDir . '/*' . self::OLD_INSTALL_EXT); + $finder = Finder::create() + ->depth(0) + ->files() + ->name('*' . self::OLD_INSTALL_EXT) + ->in($dir); + + return $finder; } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 71fd38ccd..db1fc674c 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -13,6 +13,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; +use Symfony\Component\Finder\Finder; /** * Base downloader for archives @@ -52,12 +53,13 @@ abstract class ArchiveDownloader extends FileDownloader $contentDir = $this->getFolderContent($temporaryDir); // only one dir in the archive, extract its contents out of it - if (1 === count($contentDir) && is_dir($contentDir[0])) { - $contentDir = $this->getFolderContent($contentDir[0]); + if (1 === count($contentDir) && is_dir(reset($contentDir))) { + $contentDir = $this->getFolderContent((string) reset($contentDir)); } // move files back out of the temp dir foreach ($contentDir as $file) { + $file = (string) $file; $this->filesystem->rename($file, $path . '/' . basename($file)); } @@ -133,10 +135,12 @@ abstract class ArchiveDownloader extends FileDownloader */ private function getFolderContent($dir) { - $files = array_merge($this->filesystem->realpathGlob($dir . '/.*'), $this->filesystem->realpathGlob($dir . '/*')); + $finder = Finder::create() + ->ignoreVCS(false) + ->ignoreDotFiles(false) + ->depth(0) + ->in($dir); - return array_values(array_filter($files, function ($el) { - return basename($el) !== '.' && basename($el) !== '..'; - })); + return iterator_to_array($finder); } } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index fab4c5793..b567c4b31 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -14,6 +14,7 @@ namespace Composer\Util; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; +use Symfony\Component\Finder\Finder; /** * @author Jordi Boggiano @@ -41,29 +42,6 @@ class Filesystem return false; } - /** - * Force the results of a glob to be realpaths. - * - * @param string $pattern - * @param int $flags - * @return array - */ - public function realpathGlob($pattern, $flags = 0) - { - $matches = glob($pattern, $flags); - if (!$matches) { - return array(); - } - - return array_map(function ($path) { - if (basename($path) === '.' || basename($path) === '..') { - return $path; - } - - return realpath($path); - }, $matches); - } - /** * Checks if a directory is empty * @@ -72,9 +50,13 @@ class Filesystem */ public function isDirEmpty($dir) { - $dir = rtrim($dir, '/\\'); + $finder = Finder::create() + ->ignoreVCS(false) + ->ignoreDotFiles(false) + ->depth(0) + ->in($dir); - return count($this->realpathGlob($dir.'/*')) === 0 && count($this->realpathGlob($dir.'/.*')) === 2; + return count($finder) === 0; } public function emptyDirectory($dir, $ensureDirectoryExists = true) @@ -84,13 +66,14 @@ class Filesystem } if (is_dir($dir)) { - foreach ($this->realpathGlob(rtrim($dir, '\\/').'/*') as $path) { - $this->remove($path); - } - foreach ($this->realpathGlob(rtrim($dir, '\\/').'/.*') as $path) { - if (basename($path) !== '..' && basename($path) !== '.') { - $this->remove($path); - } + $finder = Finder::create() + ->ignoreVCS(false) + ->ignoreDotFiles(false) + ->depth(0) + ->in($dir); + + foreach ($finder as $path) { + $this->remove((string) $path); } } } From d97e00643fd619cb53827b44e53679c23a6baeaa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Jun 2014 13:17:23 +0200 Subject: [PATCH 295/638] Fix typo --- src/Composer/Command/SelfUpdateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index cad41df51..13abee32b 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -217,7 +217,7 @@ EOT ->depth(0) ->files() ->name('*' . self::OLD_INSTALL_EXT) - ->in($dir); + ->in($rollbackDir); return $finder; } From d036b2390ec18a78fc00f736ba8a08718afcf431 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Jun 2014 19:36:06 +0200 Subject: [PATCH 296/638] Load root aliases for providers by package name and not by provider name, fixes #3043 --- src/Composer/Repository/ComposerRepository.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 1e84b435c..e73977271 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -418,10 +418,10 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository // handle root package aliases unset($rootAliasData); - if (isset($this->rootAliases[$name][$package->getVersion()])) { - $rootAliasData = $this->rootAliases[$name][$package->getVersion()]; - } elseif ($package instanceof AliasPackage && isset($this->rootAliases[$name][$package->getAliasOf()->getVersion()])) { - $rootAliasData = $this->rootAliases[$name][$package->getAliasOf()->getVersion()]; + if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) { + $rootAliasData = $this->rootAliases[$package->getName()][$package->getVersion()]; + } elseif ($package instanceof AliasPackage && isset($this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()])) { + $rootAliasData = $this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()]; } if (isset($rootAliasData)) { From ac497feabaa0d247c441178b7b4aaa4c61b07399 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 10 Jun 2014 16:02:44 +0200 Subject: [PATCH 297/638] CS fixes --- src/Composer/Autoload/AutoloadGenerator.php | 10 ++-- src/Composer/Autoload/ClassLoader.php | 7 +-- src/Composer/Autoload/ClassMapGenerator.php | 2 +- src/Composer/Cache.php | 3 +- src/Composer/Command/ArchiveCommand.php | 1 + src/Composer/Command/CreateProjectCommand.php | 7 ++- src/Composer/Command/InstallCommand.php | 1 + src/Composer/Config.php | 2 +- src/Composer/Config/JsonConfigSource.php | 5 +- src/Composer/DependencyResolver/Pool.php | 12 ++--- .../DependencyResolver/RuleSetGenerator.php | 48 +++++++++---------- .../DependencyResolver/RuleWatchGraph.php | 10 ++-- .../DependencyResolver/Transaction.php | 1 - src/Composer/Downloader/DownloadManager.php | 7 ++- src/Composer/Downloader/FileDownloader.php | 2 - src/Composer/Downloader/GitDownloader.php | 12 ++--- src/Composer/Downloader/GzipDownloader.php | 1 - .../Downloader/PearPackageExtractor.php | 10 ++-- .../Downloader/PerforceDownloader.php | 5 +- src/Composer/Downloader/VcsDownloader.php | 8 ++-- src/Composer/Downloader/ZipDownloader.php | 2 +- src/Composer/Factory.php | 14 +++--- src/Composer/Installer.php | 5 +- src/Composer/Json/JsonFile.php | 1 - src/Composer/Json/JsonFormatter.php | 9 ++-- src/Composer/Package/Link.php | 1 - .../Package/Loader/RootPackageLoader.php | 1 + src/Composer/Package/Locker.php | 1 - .../Repository/ArtifactRepository.php | 19 ++++---- .../Repository/ComposerRepository.php | 1 + .../Repository/PlatformRepository.php | 1 - src/Composer/Repository/Vcs/GitDriver.php | 2 +- src/Composer/Repository/Vcs/GitHubDriver.php | 1 + .../Repository/Vcs/PerforceDriver.php | 1 + src/Composer/Util/ConfigValidator.php | 2 +- src/Composer/Util/Filesystem.php | 2 +- src/Composer/Util/GitHub.php | 2 +- src/Composer/Util/Perforce.php | 20 +++++--- src/Composer/Util/ProcessExecutor.php | 8 ++-- tests/Composer/Test/AllFunctionalTest.php | 2 +- .../Test/Autoload/ClassLoaderTest.php | 6 +-- .../Test/Downloader/FileDownloaderTest.php | 2 +- .../Downloader/PerforceDownloaderTest.php | 3 ++ .../Composer/Test/Json/JsonFormatterTest.php | 3 +- .../Test/Json/JsonValidationExceptionTest.php | 4 +- .../Package/Loader/RootPackageLoaderTest.php | 8 ++-- .../Loader/ValidatingArrayLoaderTest.php | 1 + .../Package/Version/VersionParserTest.php | 2 +- .../Repository/ArtifactRepositoryTest.php | 7 ++- .../Repository/FilesystemRepositoryTest.php | 1 - .../Repository/Vcs/PerforceDriverTest.php | 2 + tests/Composer/Test/Util/PerforceTest.php | 32 ++++++------- .../Test/Util/StreamContextFactoryTest.php | 2 +- 53 files changed, 166 insertions(+), 156 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index e2eb6bf56..836acafe8 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -623,7 +623,7 @@ FOOTER; * * Packages of equal weight retain the original order * - * @param array $packageMap + * @param array $packageMap * @return array */ protected function sortPackageMap(array $packageMap) @@ -646,7 +646,7 @@ FOOTER; $computing = array(); $computed = array(); - $computeImportance = function($name) use(&$computeImportance, &$computing, &$computed, $usageList) { + $computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) { // reusing computed importance if (isset($computed[$name])) { return $computed[$name]; @@ -679,17 +679,17 @@ FOOTER; $weightList[$name] = $weight; } - $stable_sort = function(&$array) { + $stable_sort = function (&$array) { static $transform, $restore; $i = 0; if (!$transform) { - $transform = function(&$v, $k) use(&$i) { + $transform = function (&$v, $k) use (&$i) { $v = array($v, ++$i, $k, $v); }; - $restore = function(&$v, $k) { + $restore = function (&$v, $k) { $v = $v[3]; }; } diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index a71055531..88684c526 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -202,10 +202,11 @@ class ClassLoader * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories */ - public function setPsr4($prefix, $paths) { + public function setPsr4($prefix, $paths) + { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 7b977cd3d..6cb306bfb 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -28,7 +28,7 @@ class ClassMapGenerator * Generate a class map file * * @param \Traversable $dirs Directories or a single path to search in - * @param string $file The name of the class map file + * @param string $file The name of the class map file */ public static function dump($dirs, $file) { diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index f8d64cae1..7387a94cc 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -144,8 +144,7 @@ class Cache public function gc($ttl, $maxSize) { - if ($this->enabled) - { + if ($this->enabled) { $expire = new \DateTime(); $expire->modify('-'.$ttl.' seconds'); diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 7a57ca13b..d7e627b34 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -69,6 +69,7 @@ EOT if (0 === $returnCode) { $this->getComposer()->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD); } + return $returnCode; } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 8adc7e106..632e869df 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -19,7 +19,6 @@ use Composer\Installer\ProjectInstaller; use Composer\Installer\InstallationManager; use Composer\IO\IOInterface; use Composer\Package\BasePackage; -use Composer\Package\LinkConstraint\VersionConstraint; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\Repository\ComposerRepository; @@ -339,10 +338,10 @@ EOT /** * Updated preferSource or preferDist based on the preferredInstall config option - * @param Config $config + * @param Config $config * @param InputInterface $input - * @param boolean $preferSource - * @param boolean $preferDist + * @param boolean $preferSource + * @param boolean $preferDist */ protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist) { diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index ac220fdf0..4f40837f1 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -64,6 +64,7 @@ EOT { if ($args = $input->getArgument('packages')) { $output->writeln('Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.'); + return 1; } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index cfc042465..8d41f3fb1 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -269,7 +269,7 @@ class Config /** * Replaces {$refs} inside a config string * - * @param string $value a config string that can contain {$refs-to-other-config} + * @param string $value a config string that can contain {$refs-to-other-config} * @return string */ private function process($value) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 3057556e5..6ca613ea6 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -179,14 +179,15 @@ class JsonConfigSource implements ConfigSourceInterface /** * Prepend a reference to an element to the beginning of an array. * - * @param array $array - * @param mixed $value + * @param array $array + * @param mixed $value * @return array */ private function arrayUnshiftRef(&$array, &$value) { $return = array_unshift($array, ''); $array[0] =& $value; + return $return; } } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index baac24457..636a78c0a 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -227,12 +227,12 @@ class Pool /** * Searches all packages providing the given package name and match the constraint * - * @param string $name The package name to be searched for - * @param LinkConstraintInterface $constraint A constraint that all returned - * packages must match or null to return all - * @param bool $mustMatchName Whether the name of returned packages - * must match the given name - * @return array A set of packages + * @param string $name The package name to be searched for + * @param LinkConstraintInterface $constraint A constraint that all returned + * packages must match or null to return all + * @param bool $mustMatchName Whether the name of returned packages + * must match the given name + * @return array A set of packages */ public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false) { diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 5e571c73b..5bcf9a079 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -40,13 +40,13 @@ class RuleSetGenerator * This rule is of the form (-A|B|C), where B and C are the providers of * one requirement of the package A. * - * @param PackageInterface $package The package with a requirement - * @param array $providers The providers of the requirement - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param mixed $reasonData Any data, e.g. the requirement name, - * that goes with the reason - * @return Rule The generated rule or null if tautological + * @param PackageInterface $package The package with a requirement + * @param array $providers The providers of the requirement + * @param int $reason A RULE_* constant describing the + * reason for generating this rule + * @param mixed $reasonData Any data, e.g. the requirement name, + * that goes with the reason + * @return Rule The generated rule or null if tautological */ protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null) { @@ -69,10 +69,10 @@ class RuleSetGenerator * The rule is (A|B|C) with A, B and C different packages. If the given * set of packages is empty an impossible rule is generated. * - * @param array $packages The set of packages to choose from - * @param int $reason A RULE_* constant describing the reason for - * generating this rule - * @param array $job The job this rule was created from + * @param array $packages The set of packages to choose from + * @param int $reason A RULE_* constant describing the reason for + * generating this rule + * @param array $job The job this rule was created from * @return Rule The generated rule */ protected function createInstallOneOfRule(array $packages, $reason, $job) @@ -90,11 +90,11 @@ class RuleSetGenerator * * The rule for a package A is (-A). * - * @param PackageInterface $package The package to be removed - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param array $job The job this rule was created from - * @return Rule The generated rule + * @param PackageInterface $package The package to be removed + * @param int $reason A RULE_* constant describing the + * reason for generating this rule + * @param array $job The job this rule was created from + * @return Rule The generated rule */ protected function createRemoveRule(PackageInterface $package, $reason, $job) { @@ -107,13 +107,13 @@ class RuleSetGenerator * The rule for conflicting packages A and B is (-A|-B). A is called the issuer * and B the provider. * - * @param PackageInterface $issuer The package declaring the conflict - * @param PackageInterface $provider The package causing the conflict - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param mixed $reasonData Any data, e.g. the package name, that - * goes with the reason - * @return Rule The generated rule + * @param PackageInterface $issuer The package declaring the conflict + * @param PackageInterface $provider The package causing the conflict + * @param int $reason A RULE_* constant describing the + * reason for generating this rule + * @param mixed $reasonData Any data, e.g. the package name, that + * goes with the reason + * @return Rule The generated rule */ protected function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null) { @@ -262,7 +262,7 @@ class RuleSetGenerator * Adds all rules for all update packages of a given package * * @param PackageInterface $package Rules for this package's updates are to - * be added + * be added */ private function addRulesForUpdatePackages(PackageInterface $package) { diff --git a/src/Composer/DependencyResolver/RuleWatchGraph.php b/src/Composer/DependencyResolver/RuleWatchGraph.php index 627a66eb3..a9f7414b2 100644 --- a/src/Composer/DependencyResolver/RuleWatchGraph.php +++ b/src/Composer/DependencyResolver/RuleWatchGraph.php @@ -69,11 +69,11 @@ class RuleWatchGraph * above example the rule was (-A|+B), then A turning true means that * B must now be decided true as well. * - * @param int $decidedLiteral The literal which was decided (A in our example) - * @param int $level The level at which the decision took place and at which - * all resulting decisions should be made. - * @param Decisions $decisions Used to check previous decisions and to - * register decisions resulting from propagation + * @param int $decidedLiteral The literal which was decided (A in our example) + * @param int $level The level at which the decision took place and at which + * all resulting decisions should be made. + * @param Decisions $decisions Used to check previous decisions and to + * register decisions resulting from propagation * @return Rule|null If a conflict is found the conflicting rule is returned */ public function propagateLiteral($decidedLiteral, $level, $decisions) diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index 0c30f06df..214c502d1 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -13,7 +13,6 @@ namespace Composer\DependencyResolver; use Composer\Package\AliasPackage; -use Composer\DependencyResolver\Operation; /** * @author Nils Adermann diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index f2296d7d7..26c5a5301 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -13,7 +13,6 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; -use Composer\Downloader\DownloaderInterface; use Composer\IO\IOInterface; use Composer\Util\Filesystem; @@ -104,7 +103,7 @@ class DownloadManager /** * Returns downloader for a specific installation type. * - * @param string $type installation type + * @param string $type installation type * @return DownloaderInterface * * @throws \InvalidArgumentException if downloader for provided type is not registered @@ -122,12 +121,12 @@ class DownloadManager /** * Returns downloader for already installed package. * - * @param PackageInterface $package package instance + * @param PackageInterface $package package instance * @return DownloaderInterface|null * * @throws \InvalidArgumentException if package has no installation source specified * @throws \LogicException if specific downloader used to load package with - * wrong type + * wrong type */ public function getDownloaderForInstalledPackage(PackageInterface $package) { diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index fc903fd66..7d1c4f9a7 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -21,7 +21,6 @@ use Composer\Plugin\PluginEvents; use Composer\Plugin\PreFileDownloadEvent; use Composer\EventDispatcher\EventDispatcher; use Composer\Util\Filesystem; -use Composer\Util\GitHub; use Composer\Util\RemoteFilesystem; /** @@ -60,7 +59,6 @@ class FileDownloader implements DownloaderInterface $this->filesystem = $filesystem ?: new Filesystem(); $this->cache = $cache; - if ($this->cache && $this->cache->gcIsNecessary()) { $this->cache->gc($config->get('cache-files-ttl'), $config->get('cache-files-maxsize')); } diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index e33c9d790..f44b1514e 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -47,7 +47,7 @@ class GitDownloader extends VcsDownloader $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); - $commandCallable = function($url) use ($ref, $path, $command) { + $commandCallable = function ($url) use ($ref, $path, $command) { return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref)); }; @@ -77,7 +77,7 @@ class GitDownloader extends VcsDownloader $this->io->write(" Checking out ".$ref); $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; - $commandCallable = function($url) use ($command) { + $commandCallable = function ($url) use ($command) { return sprintf($command, escapeshellarg($url)); }; @@ -201,10 +201,10 @@ class GitDownloader extends VcsDownloader /** * Updates the given path to the given commit ref * - * @param string $path - * @param string $reference - * @param string $branch - * @param DateTime $date + * @param string $path + * @param string $reference + * @param string $branch + * @param DateTime $date * @return null|string if a string is returned, it is the commit reference that was checked out if the original could not be found */ protected function updateToCommit($path, $reference, $branch, $date) diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index fb4bce89d..073b18c30 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -68,4 +68,3 @@ class GzipDownloader extends ArchiveDownloader return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME); } } - diff --git a/src/Composer/Downloader/PearPackageExtractor.php b/src/Composer/Downloader/PearPackageExtractor.php index 5310c7ab7..3ed3ed7be 100644 --- a/src/Composer/Downloader/PearPackageExtractor.php +++ b/src/Composer/Downloader/PearPackageExtractor.php @@ -127,11 +127,11 @@ class PearPackageExtractor /** * Builds list of copy and list of remove actions that would transform extracted PEAR tarball into installed package. * - * @param string $source string path to extracted files - * @param array $roles array [role => roleRoot] relative root for files having that role - * @param array $vars list of values can be used for replacement tasks - * @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source - * path, and target is destination of file (also relative to $source path) + * @param string $source string path to extracted files + * @param array $roles array [role => roleRoot] relative root for files having that role + * @param array $vars list of values can be used for replacement tasks + * @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source + * path, and target is destination of file (also relative to $source path) * @throws \RuntimeException */ private function buildCopyActions($source, array $roles, $vars) diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 42f424057..683ea9f34 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -44,10 +44,10 @@ class PerforceDownloader extends VcsDownloader private function getLabelFromSourceReference($ref) { $pos = strpos($ref,'@'); - if (false !== $pos) - { + if (false !== $pos) { return substr($ref, $pos + 1); } + return null; } @@ -55,6 +55,7 @@ class PerforceDownloader extends VcsDownloader { if (!empty($this->perforce)) { $this->perforce->initializePath($path); + return; } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index b0f0b7cbc..ea7df270a 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -181,10 +181,10 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa /** * Prompt the user to check if changes should be stashed/removed or the operation aborted * - * @param PackageInterface $package - * @param string $path - * @param bool $update if true (update) the changes can be stashed and reapplied after an update, - * if false (remove) the changes should be assumed to be lost if the operation is not aborted + * @param PackageInterface $package + * @param string $path + * @param bool $update if true (update) the changes can be stashed and reapplied after an update, + * if false (remove) the changes should be assumed to be lost if the operation is not aborted * @throws \RuntimeException in case the operation must be aborted */ protected function cleanChanges(PackageInterface $package, $path, $update) diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 88c0e4346..b35d6a5af 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -45,7 +45,7 @@ class ZipDownloader extends ArchiveDownloader } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); - } catch(\Exception $e) { + } catch (\Exception $e) { $processError = 'Failed to execute ' . $command . "\n\n" . $e->getMessage(); } } diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index d65bc44a8..5c5bfd5cf 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -178,9 +178,9 @@ class Factory /** * Creates a Composer instance * - * @param IOInterface $io IO instance - * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will - * read from the default filename + * @param IOInterface $io IO instance + * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will + * read from the default filename * @param bool $disablePlugins Whether plugins should not be loaded * @throws \InvalidArgumentException * @throws \UnexpectedValueException @@ -453,10 +453,10 @@ class Factory } /** - * @param IOInterface $io IO instance - * @param mixed $config either a configuration array or a filename to read from, if null it will read from - * the default filename - * @param bool $disablePlugins Whether plugins should not be loaded + * @param IOInterface $io IO instance + * @param mixed $config either a configuration array or a filename to read from, if null it will read from + * the default filename + * @param bool $disablePlugins Whether plugins should not be loaded * @return Composer */ public static function create(IOInterface $io, $config = null, $disablePlugins = false) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 29a89a4ba..fac713226 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -26,7 +26,6 @@ use Composer\DependencyResolver\SolverProblemsException; use Composer\Downloader\DownloadManager; use Composer\EventDispatcher\EventDispatcher; use Composer\Installer\InstallationManager; -use Composer\Config; use Composer\Installer\NoopInstaller; use Composer\IO\IOInterface; use Composer\Json\JsonFile; @@ -843,7 +842,7 @@ class Installer /** * Build a regexp from a package name, expanding * globs as required * - * @param string $whiteListedPattern + * @param string $whiteListedPattern * @return string */ private function packageNameToRegexp($whiteListedPattern) @@ -1146,7 +1145,7 @@ class Installer /** * Should dependencies of whitelisted packages be updated recursively? * - * @param boolean $updateDependencies + * @param boolean $updateDependencies * @return Installer */ public function setWhitelistDependencies($updateDependencies = true) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 999fa33e8..2d35b9671 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -13,7 +13,6 @@ namespace Composer\Json; use JsonSchema\Validator; -use JsonSchema\Uri\UriRetriever; use Seld\JsonLint\JsonParser; use Seld\JsonLint\ParsingException; use Composer\Util\RemoteFilesystem; diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php index f06928e34..614683608 100644 --- a/src/Composer/Json/JsonFormatter.php +++ b/src/Composer/Json/JsonFormatter.php @@ -30,9 +30,9 @@ class JsonFormatter * Originally licensed under MIT by Dave Perrett * * - * @param string $json - * @param bool $unescapeUnicode Un escape unicode - * @param bool $unescapeSlashes Un escape slashes + * @param string $json + * @param bool $unescapeUnicode Un escape unicode + * @param bool $unescapeSlashes Un escape slashes * @return string */ public static function format($json, $unescapeUnicode, $unescapeSlashes) @@ -66,7 +66,7 @@ class JsonFormatter if ($unescapeUnicode && function_exists('mb_convert_encoding')) { // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha - $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function($match) { + $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function ($match) { $l = strlen($match[1]); if ($l % 2) { @@ -76,6 +76,7 @@ class JsonFormatter 'UCS-2BE' ); } + return $match[0]; }, $buffer); } diff --git a/src/Composer/Package/Link.php b/src/Composer/Package/Link.php index 159fa3ae9..5aba8b119 100644 --- a/src/Composer/Package/Link.php +++ b/src/Composer/Package/Link.php @@ -13,7 +13,6 @@ namespace Composer\Package; use Composer\Package\LinkConstraint\LinkConstraintInterface; -use Composer\Package\PackageInterface; /** * Represents a link between two packages, represented by their names diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 300adea17..64880d727 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -320,6 +320,7 @@ class RootPackageLoader extends ArrayLoader if ('9999999-dev' === $version) { $version = 'dev-'.$matches[3]; } + return $version; } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 674bc9484..2aa36187f 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -16,7 +16,6 @@ use Composer\Json\JsonFile; use Composer\Installer\InstallationManager; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; -use Composer\Package\AliasPackage; use Composer\Repository\ArrayRepository; use Composer\Package\Dumper\ArrayDumper; use Composer\Package\Loader\ArrayLoader; diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index b4d0d8c92..6a942ad2d 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -75,22 +75,23 @@ class ArtifactRepository extends ArrayRepository } /** - * Find a file by name, returning the one that has the shortest path. - * - * @param \ZipArchive $zip + * Find a file by name, returning the one that has the shortest path. + * + * @param \ZipArchive $zip * @param $filename * @return bool|int */ - private function locateFile(\ZipArchive $zip, $filename) { + private function locateFile(\ZipArchive $zip, $filename) + { $indexOfShortestMatch = false; $lengthOfShortestMatch = -1; - for ($i = 0; $i < $zip->numFiles; $i++ ){ + for ($i = 0; $i < $zip->numFiles; $i++) { $stat = $zip->statIndex($i); - if (strcmp(basename($stat['name']), $filename) === 0){ + if (strcmp(basename($stat['name']), $filename) === 0) { $directoryName = dirname($stat['name']); if ($directoryName == '.') { - //if composer.json is in root directory + //if composer.json is in root directory //it has to be the one to use. return $i; } @@ -100,7 +101,7 @@ class ArtifactRepository extends ArrayRepository //composer.json files below first directory are rejected continue; } - + $length = strlen($stat['name']); if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) { //Check it's not a directory. @@ -115,7 +116,7 @@ class ArtifactRepository extends ArrayRepository return $indexOfShortestMatch; } - + private function getComposerInformation(\SplFileInfo $file) { $zip = new \ZipArchive(); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index e73977271..82d8b9a36 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -275,6 +275,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository foreach ($package->getDistUrls() as $url) { if (strpos($url, $this->baseUrl) === 0) { $package->setTransportOptions($this->options); + return; } } diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 0c6a5d9ce..636cba16b 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -161,7 +161,6 @@ class PlatformRepository extends ArrayRepository } } - private function buildPackageName($name) { return 'ext-' . str_replace(' ', '-', $name); diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 0e6286906..f37be1e9a 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -67,7 +67,7 @@ class GitDriver extends VcsDriver $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs); $repoDir = $this->repoDir; - $commandCallable = function($url) use ($repoDir) { + $commandCallable = function ($url) use ($repoDir) { return sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($repoDir)); }; diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 0dc8a7eee..bb9e34fc7 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -54,6 +54,7 @@ class GitHubDriver extends VcsDriver if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) { $this->setupGitDriver($this->url); + return; } diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index e928e5835..7e313bf63 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -140,6 +140,7 @@ class PerforceDriver extends VcsDriver { $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); $this->composerInfoIdentifier = $identifier; + return !empty($this->composerInfo); } diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 5b9a7cf26..5b33e35ae 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -37,7 +37,7 @@ class ConfigValidator /** * Validates the config, and returns the result. * - * @param string $file The path to the file + * @param string $file The path to the file * @param integer $arrayLoaderValidationFlags Flags for ArrayLoader validation * * @return array a triple containing the errors, publishable errors, and warnings diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index b567c4b31..798cd9e44 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -384,7 +384,7 @@ class Filesystem /** * Return if the given path is local * - * @param string $path + * @param string $path * @return bool */ public static function isLocalPath($path) diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index a0b803db7..784cb51c9 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -150,7 +150,7 @@ class GitHub if (in_array($e->getCode(), array(403, 401))) { // 401 when authentication was supplied, handle 2FA if required. if ($this->io->hasAuthentication($originUrl)) { - $headerNames = array_map(function($header) { + $headerNames = array_map(function ($header) { return strtolower(strstr($header, ':', true)); }, $e->getHeaders()); diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 894a151ef..d3dcd696f 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -53,12 +53,14 @@ class Perforce { $isWindows = defined('PHP_WINDOWS_VERSION_BUILD'); $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io); + return $perforce; } public static function checkServerExists($url, ProcessExecutor $processExecutor) { $output = null; + return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output); } @@ -119,6 +121,7 @@ class Perforce { $this->commandResult = ""; $exit_code = $this->process->execute($command, $this->commandResult); + return $exit_code; } @@ -248,6 +251,7 @@ class Perforce $command = 'echo $' . $name; $this->executeCommand($command); $result = trim($this->commandResult); + return $result; } } @@ -283,18 +287,19 @@ class Perforce { $command = $this->generateP4Command('login -s', false); $exitCode = $this->executeCommand($command); - if ($exitCode){ + if ($exitCode) { $errorOutput = $this->process->getErrorOutput(); $index = strpos($errorOutput, $this->getUser()); - if ($index === false){ + if ($index === false) { $index = strpos($errorOutput, 'p4'); - if ($index===false){ + if ($index === false) { return false; } throw new \Exception('p4 command not found in path: ' . $errorOutput); } throw new \Exception('Invalid user name: ' . $this->getUser() ); } + return true; } @@ -384,7 +389,7 @@ class Perforce $command = 'echo ' . $password . ' | ' . $this->generateP4Command(' login -a', false); $exitCode = $this->executeCommand($command); $result = trim($this->commandResult); - if ($exitCode){ + if ($exitCode) { throw new \Exception("Error logging in:" . $this->process->getErrorOutput()); } } @@ -471,6 +476,7 @@ class Perforce $lastCommitNum = $lastCommitArr[1]; $branches = array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum); + return $branches; } @@ -488,6 +494,7 @@ class Perforce $tags[$fields[1]] = $this->getStream() . '@' . $fields[1]; } } + return $tags; } @@ -552,14 +559,13 @@ class Perforce public function getFilesystem() { - if (empty($this->filesystem)) - { + if (empty($this->filesystem)) { $this->filesystem = new Filesystem($this->process); } + return $this->filesystem; } - public function setFilesystem(Filesystem $fs) { $this->filesystem = $fs; diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index 1ad2b1dfa..ab4e4b2db 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -34,10 +34,10 @@ class ProcessExecutor /** * runs a process on the commandline * - * @param string $command the command to execute - * @param mixed $output the output will be written into this var if passed by ref - * if a callable is passed it will be used as output handler - * @param string $cwd the working directory + * @param string $command the command to execute + * @param mixed $output the output will be written into this var if passed by ref + * if a callable is passed it will be used as output handler + * @param string $cwd the working directory * @return int statuscode */ public function execute($command, &$output = null, $cwd = null) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index 03ec4305f..f27d58117 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -124,7 +124,7 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase $testDir = sys_get_temp_dir().'/composer_functional_test'.uniqid(mt_rand(), true); $this->testDir = $testDir; $varRegex = '#%([a-zA-Z_-]+)%#'; - $variableReplacer = function($match) use (&$data, $testDir) { + $variableReplacer = function ($match) use (&$data, $testDir) { list(, $var) = $match; switch ($var) { diff --git a/tests/Composer/Test/Autoload/ClassLoaderTest.php b/tests/Composer/Test/Autoload/ClassLoaderTest.php index 0b9c1934e..eee942e42 100644 --- a/tests/Composer/Test/Autoload/ClassLoaderTest.php +++ b/tests/Composer/Test/Autoload/ClassLoaderTest.php @@ -14,9 +14,9 @@ class ClassLoaderTest extends \PHPUnit_Framework_TestCase * * @dataProvider getLoadClassTests * - * @param string $class The fully-qualified class name to test, without preceding namespace separator. - * @param bool $prependSeparator Whether to call ->loadClass() with a class name with preceding - * namespace separator, as it happens in PHP 5.3.0 - 5.3.2. See https://bugs.php.net/50731 + * @param string $class The fully-qualified class name to test, without preceding namespace separator. + * @param bool $prependSeparator Whether to call ->loadClass() with a class name with preceding + * namespace separator, as it happens in PHP 5.3.0 - 5.3.2. See https://bugs.php.net/50731 */ public function testLoadClass($class, $prependSeparator = FALSE) { diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php index ffdbf997d..f0578f6be 100644 --- a/tests/Composer/Test/Downloader/FileDownloaderTest.php +++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php @@ -109,7 +109,7 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase $ioMock = $this->getMock('Composer\IO\IOInterface'); $ioMock->expects($this->any()) ->method('write') - ->will($this->returnCallback(function($messages, $newline = true) use ($path) { + ->will($this->returnCallback(function ($messages, $newline = true) use ($path) { if (is_file($path.'/script.js')) { unlink($path.'/script.js'); } diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index b1d3d8fb9..9e26689a9 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -65,6 +65,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $config = new Config(); $settings = array('config' => array('home' => $this->testPath)); $config->merge($settings); + return $config; } @@ -77,6 +78,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase { $package = $this->getMock('Composer\Package\PackageInterface'); $package->expects($this->any())->method('getRepository')->will($this->returnValue($repository)); + return $package; } @@ -92,6 +94,7 @@ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase $args = array($repoConfig, $io, $config); $repository = $this->getMock($class, $methods, $args); $repository->expects($this->any())->method('getRepoConfig')->will($this->returnValue($repoConfig)); + return $repository; } diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php index ce7ceefa6..eb6e87b00 100644 --- a/tests/Composer/Test/Json/JsonFormatterTest.php +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -34,7 +34,7 @@ class JsonFormatterTest extends \PHPUnit_Framework_TestCase /** * Convert string to character codes split by a plus sign - * @param string $string + * @param string $string * @return string */ protected function getCharacterCodes($string) @@ -43,6 +43,7 @@ class JsonFormatterTest extends \PHPUnit_Framework_TestCase for ($i = 0; $i < strlen($string); $i++) { $codes[] = ord($string[$i]); } + return implode('+', $codes); } diff --git a/tests/Composer/Test/Json/JsonValidationExceptionTest.php b/tests/Composer/Test/Json/JsonValidationExceptionTest.php index 38486a2a4..76959d688 100644 --- a/tests/Composer/Test/Json/JsonValidationExceptionTest.php +++ b/tests/Composer/Test/Json/JsonValidationExceptionTest.php @@ -25,13 +25,13 @@ class JsonValidationExceptionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($message, $object->getMessage()); $this->assertEquals($errors, $object->getErrors()); } - + public function testGetErrorsWhenNoErrorsProvided() { $object = new JsonValidationException('test message'); $this->assertEquals(array(), $object->getErrors()); } - + public function errorProvider() { return array( diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php index 1a6a3bf78..51799d053 100644 --- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php @@ -35,7 +35,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $self = $this; /* Can do away with this mock object when https://github.com/sebastianbergmann/phpunit-mock-objects/issues/81 is fixed */ - $processExecutor = new ProcessExecutorMock(function($command, &$output = null, $cwd = null) use ($self, $commitHash) { + $processExecutor = new ProcessExecutorMock(function ($command, &$output = null, $cwd = null) use ($self, $commitHash) { if (0 === strpos($command, 'git describe')) { // simulate not being on a tag return 1; @@ -69,7 +69,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $self = $this; /* Can do away with this mock object when https://github.com/sebastianbergmann/phpunit-mock-objects/issues/81 is fixed */ - $processExecutor = new ProcessExecutorMock(function($command, &$output = null, $cwd = null) use ($self) { + $processExecutor = new ProcessExecutorMock(function ($command, &$output = null, $cwd = null) use ($self) { $self->assertEquals('git describe --exact-match --tags', $command); $output = "v2.0.5-alpha2"; @@ -98,7 +98,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $self = $this; /* Can do away with this mock object when https://github.com/sebastianbergmann/phpunit-mock-objects/issues/81 is fixed */ - $processExecutor = new ProcessExecutorMock(function($command, &$output = null, $cwd = null) use ($self) { + $processExecutor = new ProcessExecutorMock(function ($command, &$output = null, $cwd = null) use ($self) { if ('git describe --exact-match --tags' === $command) { $output = "foo-bar"; @@ -124,7 +124,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $processExecutor = new ProcessExecutorMock(function($command, &$output = null, $cwd = null) { + $processExecutor = new ProcessExecutorMock(function ($command, &$output = null, $cwd = null) { return 1; }); diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 593598a58..23c47c3c7 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -198,6 +198,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase { if (!$mustCheck) { $this->assertTrue(true); + return; } $internalLoader = $this->getMock('Composer\Package\Loader\LoaderInterface'); diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 18b5c493f..464779d83 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -54,7 +54,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase ); $self = $this; - $createPackage = function($arr) use ($self) { + $createPackage = function ($arr) use ($self) { $package = $self->getMock('\Composer\Package\PackageInterface'); $package->expects($self->once())->method('isDev')->will($self->returnValue(true)); $package->expects($self->once())->method('getSourceType')->will($self->returnValue('git')); diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index a5889dd28..7833899e0 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -28,14 +28,14 @@ class ArtifactRepositoryTest extends TestCase 'vendor3/package1-5.4.3', 'test/jsonInRoot-1.0.0', 'test/jsonInFirstLevel-1.0.0', - //The files not-an-artifact.zip and jsonSecondLevel are not valid + //The files not-an-artifact.zip and jsonSecondLevel are not valid //artifacts and do not get detected. ); $coordinates = array('type' => 'artifact', 'url' => __DIR__ . '/Fixtures/artifacts'); $repo = new ArtifactRepository($coordinates, new NullIO(), new Config()); - $foundPackages = array_map(function(BasePackage $package) { + $foundPackages = array_map(function (BasePackage $package) { return "{$package->getPrettyName()}-{$package->getPrettyVersion()}"; }, $repo->getPackages()); @@ -73,7 +73,7 @@ class ArtifactRepositoryTest extends TestCase // ), //); // -//foreach($archivesToCreate as $archiveName => $fileDetails) { +//foreach ($archivesToCreate as $archiveName => $fileDetails) { // $zipFile = new ZipArchive(); // $zipFile->open("$archiveName.zip", ZIPARCHIVE::CREATE); // @@ -83,4 +83,3 @@ class ArtifactRepositoryTest extends TestCase // // $zipFile->close(); //} - diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php index fa6214dee..fa1ec6d5b 100644 --- a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php @@ -12,7 +12,6 @@ namespace Composer\Repository; -use Composer\Repository\FilesystemRepository; use Composer\TestCase; class FilesystemRepositoryTest extends TestCase diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index dcb50244a..09762f26e 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -74,6 +74,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase { $config = new Config(); $config->merge(array('config'=>array('home'=>$testPath))); + return $config; } @@ -104,6 +105,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase protected function getMockPerforce() { $methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation', 'cleanupClientSpec'); + return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock(); } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index a5f3e60fa..91575b06d 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -139,9 +139,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $this->createNewPerforceWithWindowsFlag(true); $this->perforce->setUser(null); $expectedCommand = 'p4 set'; - $callback = function($command, &$output) - { + $callback = function ($command, &$output) { $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL; + return true; }; $this->processExecutor->expects($this->at(0)) @@ -157,9 +157,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $this->createNewPerforceWithWindowsFlag(false); $this->perforce->setUser(null); $expectedCommand = 'echo $P4USER'; - $callback = function($command, &$output) - { + $callback = function ($command, &$output) { $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; + return true; }; $this->processExecutor->expects($this->at(0)) @@ -233,9 +233,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { $this->createNewPerforceWithWindowsFlag(true); $expectedCommand = 'p4 set'; - $callback = function($command, &$output) - { + $callback = function ($command, &$output) { $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; }; $this->processExecutor->expects($this->at(0)) @@ -250,9 +250,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase { $this->createNewPerforceWithWindowsFlag(false); $expectedCommand = 'echo $P4PASSWD'; - $callback = function($command, &$output) - { + $callback = function ($command, &$output) { $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; }; $this->processExecutor->expects($this->at(0)) @@ -357,9 +357,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase ) ); $expectedCommand2 = 'p4 -u user -p port changes //depot/branch/...'; - $expectedCallback = function($command, &$output) - { + $expectedCallback = function ($command, &$output) { $output = 'Change 1234 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; }; $this->processExecutor->expects($this->at(1)) @@ -374,9 +374,9 @@ class PerforceTest extends \PHPUnit_Framework_TestCase public function testGetBranchesWithoutStream() { $expectedCommand = 'p4 -u user -p port changes //depot/...'; - $expectedCallback = function($command, &$output) - { + $expectedCallback = function ($command, &$output) { $output = 'Change 5678 on 2014/03/19 by Clark.Stuth@Clark.Stuth_test_client \'test changelist\''; + return true; }; $this->processExecutor->expects($this->once()) @@ -629,12 +629,12 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $result = $this->perforce->checkServerExists('perforce.does.exist:port', $processExecutor); $this->assertTrue($result); } - + /** * Test if "p4" command is missing. - * + * * @covers \Composer\Util\Perforce::checkServerExists - * + * * @return void */ public function testCheckServerClientError() @@ -646,7 +646,7 @@ class PerforceTest extends \PHPUnit_Framework_TestCase ->method('execute') ->with($this->equalTo($expectedCommand), $this->equalTo(null)) ->will($this->returnValue(127)); - + $result = $this->perforce->checkServerExists('perforce.does.exist:port', $processExecutor); $this->assertFalse($result); } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index b0923e6df..5415a8c11 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -52,7 +52,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase ), array( $a = array('http' => array('method' => 'GET', 'max_redirects' => 20, 'follow_location' => 1)), array('http' => array('method' => 'GET')), - array('options' => $a, 'notification' => $f = function() {}), array('notification' => $f) + array('options' => $a, 'notification' => $f = function () {}), array('notification' => $f) ), ); } From 845ebdcfd47d167730c356ec9c9f4db783bcb068 Mon Sep 17 00:00:00 2001 From: Maximilian Reichel Date: Thu, 12 Jun 2014 13:55:56 +0200 Subject: [PATCH 298/638] alter command help to explain glob usage hey there! using globs for whitelisting packages is (imo) a very useful feature worth being explained to the user ;) --- src/Composer/Command/UpdateCommand.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index c3c90b94d..d45e386ed 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -58,6 +58,11 @@ To limit the update operation to a few packages, you can list the package(s) you want to update as such: php composer.phar update vendor/package1 foo/mypackage [...] + +You may also use an asterisk (*) pattern to limit the update operation to package(s) +from a specific vendor: + +php composer.phar update vendor/package1 foo/* [...] EOT ) ; From 24f0f3254398d12b1aad63841b902a0071829ad2 Mon Sep 17 00:00:00 2001 From: Josef Martinec Date: Thu, 19 Jun 2014 17:06:31 +0200 Subject: [PATCH 299/638] Skip external symlinks when creating PHAR archive --- src/Composer/Package/Archiver/ArchivableFilesFinder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/src/Composer/Package/Archiver/ArchivableFilesFinder.php index f6cadbe21..44c682616 100644 --- a/src/Composer/Package/Archiver/ArchivableFilesFinder.php +++ b/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -52,6 +52,10 @@ class ArchivableFilesFinder extends \FilterIterator $this->finder = new Finder\Finder(); $filter = function (\SplFileInfo $file) use ($sources, $filters, $fs) { + if ($file->isLink() && strpos($file->getLinkTarget(), $sources) !== 0) { + return false; + } + $relativePath = preg_replace( '#^'.preg_quote($sources, '#').'#', '', From dcea95feba241d65f075b1e806af24b6291a1235 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 22 Jun 2014 10:52:33 +0200 Subject: [PATCH 300/638] Update brew instructions, closes #2919 --- doc/00-intro.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 343546269..ea0ea63b0 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -112,10 +112,10 @@ Composer is part of the homebrew-php project. ```sh brew update -brew tap josegonzalez/homebrew-php +brew tap homebrew/homebrew-php brew tap homebrew/versions brew install php55-intl -brew install josegonzalez/php/composer +brew install homebrew/php/composer ``` ## Installation - Windows From 3874a11238cb425f984dcc9157db5f7e5bdd60b6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 22 Jun 2014 11:00:31 +0200 Subject: [PATCH 301/638] Update json test --- tests/Composer/Test/Json/JsonFileTest.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 4c96720da..f8fdb66ed 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -131,7 +131,17 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testFormatEmptyArray() { $data = array('test' => array(), 'test2' => new \stdClass); - $json = '{ + if ( + (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50429) + || (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50513) + || (PHP_VERSION_ID >= 50600) + ) { + $json = '{ + "test": [], + "test2": {} +}'; + } else { + $json = '{ "test": [ ], @@ -139,6 +149,8 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase } }'; + + } $this->assertJsonFormat($json, $data); } From 5a4ead4290c08714c981a340728dc9920838c80c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 22 Jun 2014 14:07:38 -0400 Subject: [PATCH 302/638] Add missing tap for homebrew installation --- doc/00-intro.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/00-intro.md b/doc/00-intro.md index ea0ea63b0..f86ee400f 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -113,6 +113,7 @@ Composer is part of the homebrew-php project. ```sh brew update brew tap homebrew/homebrew-php +brew tap homebrew/dupes brew tap homebrew/versions brew install php55-intl brew install homebrew/php/composer From b7402c3b2dc3292168726509b8886292495f06f7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 28 Jun 2014 20:23:22 +0200 Subject: [PATCH 303/638] Ignore gh-pages branches, fixes #3073 --- src/Composer/Repository/Vcs/GitHubDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index bb9e34fc7..5ab590aea 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -227,11 +227,15 @@ class GitHubDriver extends VcsDriver $this->branches = array(); $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100'; + $branchBlacklist = array('gh-pages'); + do { $branchData = JsonFile::parseJson($this->getContents($resource), $resource); foreach ($branchData as $branch) { $name = substr($branch['ref'], 11); - $this->branches[$name] = $branch['object']['sha']; + if (!in_array($name, $branchBlacklist)) { + $this->branches[$name] = $branch['object']['sha']; + } } $resource = $this->getNextPage(); From 8881cec1742cc7e56bfc0e33b08f785ae53b78dc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 12:26:48 +0200 Subject: [PATCH 304/638] Simplify version ranges (hopefully includes hhvm), refs #3049 --- tests/Composer/Test/Json/JsonFileTest.php | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index f8fdb66ed..6e0c46f12 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -131,16 +131,7 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testFormatEmptyArray() { $data = array('test' => array(), 'test2' => new \stdClass); - if ( - (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50429) - || (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50513) - || (PHP_VERSION_ID >= 50600) - ) { - $json = '{ - "test": [], - "test2": {} -}'; - } else { + if (PHP_VERSION_ID < 50429 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50513)) { $json = '{ "test": [ @@ -149,7 +140,11 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase } }'; - + } else { + $json = '{ + "test": [], + "test2": {} +}'; } $this->assertJsonFormat($json, $data); } From 24ef8698158110a05a96dcb774dda5a27ca383a8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 12:49:11 +0200 Subject: [PATCH 305/638] Remove symlinks instead of recursing into them when deleting, fixes #3050 --- src/Composer/Util/Filesystem.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 798cd9e44..3b65ac304 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -61,6 +61,10 @@ class Filesystem public function emptyDirectory($dir, $ensureDirectoryExists = true) { + if (file_exists($dir) && is_link($dir)) { + unlink($dir); + } + if ($ensureDirectoryExists) { $this->ensureDirectoryExists($dir); } @@ -89,6 +93,10 @@ class Filesystem */ public function removeDirectory($directory) { + if (file_exists($dir) && is_link($directory)) { + return unlink($directory); + } + if (!is_dir($directory)) { return true; } From f79b34c1df16206ebc4b689d7aeaf3f3bd70d6c8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 13:00:57 +0200 Subject: [PATCH 306/638] Fix typo --- src/Composer/Util/Filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 3b65ac304..e9736c007 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -93,7 +93,7 @@ class Filesystem */ public function removeDirectory($directory) { - if (file_exists($dir) && is_link($directory)) { + if (file_exists($directory) && is_link($directory)) { return unlink($directory); } From d9fe17443b0daeae249b6b02b267f1ba94b59d58 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 13:11:27 +0200 Subject: [PATCH 307/638] Allow empty json files to be merged into a config, fixes #3055 --- src/Composer/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 8d41f3fb1..f22604ee3 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -90,7 +90,7 @@ class Config * * @param array $config */ - public function merge(array $config) + public function merge($config) { // override defaults with given config if (!empty($config['config']) && is_array($config['config'])) { From 449f8165ef64829dc34be836235ceb74d9227dd7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 14:47:43 +0200 Subject: [PATCH 308/638] Allow $HOME and ~ usage in all *-dir config values, fixes #3060 --- doc/04-schema.md | 3 ++- src/Composer/Config.php | 5 ++++- tests/Composer/Test/ConfigTest.php | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 8ff2db7f2..e0db3c9af 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -760,7 +760,8 @@ The following options are supported: against them. For example using `{"example.org": {"username": "alice", "password": "foo"}` as the value of this option will let composer authenticate against example.org. * **vendor-dir:** Defaults to `vendor`. You can install dependencies into a - different directory if you want to. + different directory if you want to. `$HOME` and `~` will be replaced by your + home directory's path in vendor-dir and all `*-dir` options below. * **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they will be symlinked into this directory. * **cache-dir:** Defaults to `$home/cache` on unix systems and diff --git a/src/Composer/Config.php b/src/Composer/Config.php index f22604ee3..d5b41efe2 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -158,7 +158,10 @@ class Config // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_')); - return rtrim($this->process(getenv($env) ?: $this->config[$key]), '/\\'); + $val = rtrim($this->process(getenv($env) ?: $this->config[$key]), '/\\'); + $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val); + + return $val; case 'cache-ttl': return (int) $this->config[$key]; diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index c2cf82ca7..5afbabffa 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -109,6 +109,18 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $config->get('github-oauth')); } + public function testVarReplacement() + { + $config = new Config(); + $config->merge(array('config' => array('a' => 'b', 'c' => '{$a}'))); + $config->merge(array('config' => array('bin-dir' => '$HOME', 'cache-dir' => '~/foo/'))); + + $home = rtrim(getenv('HOME'), '\\/'); + $this->assertEquals('b', $config->get('c')); + $this->assertEquals($home.'/', $config->get('bin-dir')); + $this->assertEquals($home.'/foo', $config->get('cache-dir')); + } + public function testOverrideGithubProtocols() { $config = new Config(); From b232a31815ebda6e6b2cfcafa3249f6aeff33903 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 15:57:19 +0200 Subject: [PATCH 309/638] Fix --no-plugins support in installation manager, fixes #3031 --- src/Composer/Installer/InstallationManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 21b16e2fd..a43acbbda 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -14,7 +14,6 @@ namespace Composer\Installer; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; -use Composer\Plugin\PluginInstaller; use Composer\Repository\RepositoryInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\DependencyResolver\Operation\OperationInterface; From f53994fcf26817d6367f2e63f9e3150eed37ec71 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 16:17:44 +0200 Subject: [PATCH 310/638] Only lines starting with a # should be treated as comments, fixes #3066 --- .../Package/Archiver/BaseExcludeFilter.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index dca24eb67..d724f31e4 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -82,17 +82,14 @@ abstract class BaseExcludeFilter function ($line) use ($lineParser) { $line = trim($line); - $commentHash = strpos($line, '#'); - if ($commentHash !== false) { - $line = substr($line, 0, $commentHash); + if (!$line || 0 === strpos($line, '#')) { + return; } - if ($line) { - return call_user_func($lineParser, $line); - } - - return null; - }, $lines), + return call_user_func($lineParser, $line); + }, + $lines + ), function ($pattern) { return $pattern !== null; } From 745dcbce3317f7119575c39cef2cb601f9c5ffcf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Jun 2014 18:49:45 +0200 Subject: [PATCH 311/638] Fix file deletions to always use a delayed retry on windows, fixes #3074 --- src/Composer/Cache.php | 6 +- src/Composer/Downloader/ArchiveDownloader.php | 2 +- src/Composer/Downloader/FileDownloader.php | 5 +- src/Composer/Downloader/VcsDownloader.php | 5 +- src/Composer/Installer/LibraryInstaller.php | 4 +- src/Composer/Installer/PearInstaller.php | 2 +- src/Composer/Util/Filesystem.php | 70 ++++++++++++++++--- 7 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 7387a94cc..f79642066 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -136,7 +136,7 @@ class Cache { $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file); if ($this->enabled && file_exists($this->root . $file)) { - return unlink($this->root . $file); + return $this->filesystem->unlink($this->root . $file); } return false; @@ -150,7 +150,7 @@ class Cache $finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s')); foreach ($finder as $file) { - unlink($file->getPathname()); + $this->filesystem->unlink($file->getPathname()); } $totalSize = $this->filesystem->size($this->root); @@ -159,7 +159,7 @@ class Cache while ($totalSize > $maxSize && $iterator->valid()) { $filepath = $iterator->current()->getPathname(); $totalSize -= $this->filesystem->size($filepath); - unlink($filepath); + $this->filesystem->unlink($filepath); $iterator->next(); } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index db1fc674c..7c0a761c6 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -48,7 +48,7 @@ abstract class ArchiveDownloader extends FileDownloader throw $e; } - unlink($fileName); + $this->filesystem->unlink($fileName); $contentDir = $this->getFolderContent($temporaryDir); diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 7d1c4f9a7..793ae6be0 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -205,10 +205,7 @@ class FileDownloader implements DownloaderInterface { $this->io->write(" - Removing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")"); if (!$this->filesystem->removeDirectory($path)) { - // retry after a bit on windows since it tends to be touchy with mass removals - if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(250000) && !$this->filesystem->removeDirectory($path))) { - throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); - } + throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index ea7df270a..e653794ca 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -162,10 +162,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $this->cleanChanges($package, $path, false); if (!$this->filesystem->removeDirectory($path)) { - // retry after a bit on windows since it tends to be touchy with mass removals - if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(250) && !$this->filesystem->removeDirectory($path))) { - throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); - } + throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index b847eac69..4facfd494 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -259,10 +259,10 @@ class LibraryInstaller implements InstallerInterface foreach ($binaries as $bin) { $link = $this->binDir.'/'.basename($bin); if (is_link($link) || file_exists($link)) { - unlink($link); + $this->filesystem->unlink($link); } if (file_exists($link.'.bat')) { - unlink($link.'.bat'); + $this->filesystem->unlink($link.'.bat'); } } } diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php index b44f61f14..defadd9cf 100644 --- a/src/Composer/Installer/PearInstaller.php +++ b/src/Composer/Installer/PearInstaller.php @@ -77,7 +77,7 @@ class PearInstaller extends LibraryInstaller if ($this->io->isVerbose()) { $this->io->write(' Cleaning up'); } - unlink($packageArchive); + $this->filesystem->unlink($packageArchive); } protected function getBinaries(PackageInterface $package) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index e9736c007..d167de463 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -36,7 +36,7 @@ class Filesystem } if (file_exists($file)) { - return unlink($file); + return $this->unlink($file); } return false; @@ -62,7 +62,7 @@ class Filesystem public function emptyDirectory($dir, $ensureDirectoryExists = true) { if (file_exists($dir) && is_link($dir)) { - unlink($dir); + $this->unlink($dir); } if ($ensureDirectoryExists) { @@ -94,10 +94,10 @@ class Filesystem public function removeDirectory($directory) { if (file_exists($directory) && is_link($directory)) { - return unlink($directory); + return $this->unlink($directory); } - if (!is_dir($directory)) { + if (!file_exists($directory) || !is_dir($directory)) { return true; } @@ -117,11 +117,11 @@ class Filesystem $result = $this->getProcess()->execute($cmd, $output) === 0; - if ($result) { - // clear stat cache because external processes aren't tracked by the php stat cache - clearstatcache(); + // clear stat cache because external processes aren't tracked by the php stat cache + clearstatcache(); - return !is_dir($directory); + if ($result && !file_exists($directory)) { + return true; } return $this->removeDirectoryPhp($directory); @@ -144,13 +144,13 @@ class Filesystem foreach ($ri as $file) { if ($file->isDir()) { - rmdir($file->getPathname()); + $this->rmdir($file->getPathname()); } else { - unlink($file->getPathname()); + $this->unlink($file->getPathname()); } } - return rmdir($directory); + return $this->rmdir($directory); } public function ensureDirectoryExists($directory) @@ -169,6 +169,54 @@ class Filesystem } } + /** + * Attempts to unlink a file and in case of failure retries after 350ms on windows + * + * @param string $path + * @return bool + */ + public function unlink($path) + { + if (!@unlink($path)) { + // retry after a bit on windows since it tends to be touchy with mass removals + if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@unlink($path))) { + $error = error_get_last(); + $message = 'Could not delete '.$path.': ' . @$error['message']; + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"; + } + + throw new \RuntimeException($message); + } + } + + return true; + } + + /** + * Attempts to rmdir a file and in case of failure retries after 350ms on windows + * + * @param string $path + * @return bool + */ + public function rmdir($path) + { + 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))) { + $error = error_get_last(); + $message = 'Could not delete '.$path.': ' . @$error['message']; + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"; + } + + throw new \RuntimeException($message); + } + } + + return true; + } + /** * Copy then delete is a non-atomic version of {@link rename}. * From 367654b81ffd13bf3201b0a7ca1b015b754dc166 Mon Sep 17 00:00:00 2001 From: "Michael C." Date: Tue, 1 Jul 2014 20:52:28 +0100 Subject: [PATCH 312/638] Add note about how to put binaries in project root --- doc/articles/vendor-binaries.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/articles/vendor-binaries.md b/doc/articles/vendor-binaries.md index 75087b5b8..b65b6bcf4 100644 --- a/doc/articles/vendor-binaries.md +++ b/doc/articles/vendor-binaries.md @@ -111,3 +111,5 @@ An example of the former looks like this: Running `composer install` for this `composer.json` will result in all of the vendor binaries being installed in `scripts/` instead of `vendor/bin/`. + +You can set `bin-dir` to `./` to put binaries in your project root. From e77435cd0c984e2031d915a6b42648e7b284dd5c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 2 Jul 2014 17:31:13 +0200 Subject: [PATCH 313/638] Skip apc check for hhvm, fixes #3081 --- src/Composer/Command/DiagnoseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 2661837a3..3994138f4 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -312,7 +312,7 @@ EOT $warnings['openssl'] = true; } - if (ini_get('apc.enable_cli')) { + if (!defined('HHVM_VERSION') && ini_get('apc.enable_cli')) { $warnings['apc_cli'] = true; } From 526a4d7c9e2be2d7bdfbc86aaa7c25c47bbcf52b Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Sat, 5 Jul 2014 13:49:45 +0100 Subject: [PATCH 314/638] read and discard zero chars to initialise STDIN Resolves issue in cygwin on windows 8.1 where password field is skipped. See later comments of issue #2831. --- src/Composer/IO/ConsoleIO.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index c7703edfc..07e529666 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -177,7 +177,7 @@ class ConsoleIO extends BaseIO // use bash if it's present if ($finder->find('bash') && $finder->find('stty')) { $this->write($question, false); - $value = rtrim(shell_exec('bash -c "stty -echo; read -r mypassword; stty echo; echo $mypassword"')); + $value = rtrim(shell_exec('bash -c "stty -echo; read -n0 discard; read -r mypassword; stty echo; echo $mypassword"')); $this->write(''); return $value; From d47d9eb25380ed5bc4b08a8ab664fb62c69f2bbb Mon Sep 17 00:00:00 2001 From: David Zuelke Date: Mon, 7 Jul 2014 19:51:00 +0200 Subject: [PATCH 315/638] remember prefer-stable setting in composer.lock --- src/Composer/Installer.php | 5 +++-- src/Composer/Package/Locker.php | 10 +++++++++- .../Fixtures/installer/install-dev-using-dist.test | 1 + .../Fixtures/installer/install-from-empty-lock.test | 3 ++- .../installer/install-missing-alias-from-lock.test | 3 ++- ...ial-update-downgrades-non-whitelisted-unstable.test | 2 ++ .../Fixtures/installer/partial-update-from-lock.test | 2 ++ .../installer/partial-update-without-lock.test | 1 + .../Test/Fixtures/installer/update-alias-lock.test | 4 +++- .../installer/update-whitelist-reads-lock.test | 3 ++- .../updating-dev-from-lock-removes-old-deps.test | 3 ++- .../updating-dev-updates-url-and-reference.test | 4 +++- tests/Composer/Test/Package/LockerTest.php | 5 +++-- 13 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index fac713226..eb4db08c4 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -277,7 +277,8 @@ class Installer $platformDevReqs, $aliases, $this->package->getMinimumStability(), - $this->package->getStabilityFlags() + $this->package->getStabilityFlags(), + $this->package->getPreferStable() ); if ($updatedLock) { $this->io->write('Writing lock file'); @@ -647,7 +648,7 @@ class Installer private function createPolicy() { - return new DefaultPolicy($this->package->getPreferStable()); + return new DefaultPolicy((!$this->update && $this->locker->isLocked()) ? $this->locker->getPreferStable() : $this->package->getPreferStable()); } private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 2aa36187f..bee955288 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -173,6 +173,13 @@ class Locker return isset($lockData['stability-flags']) ? $lockData['stability-flags'] : array(); } + public function getPreferStable() + { + $lockData = $this->getLockData(); + + return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : false; + } + public function getAliases() { $lockData = $this->getLockData(); @@ -206,7 +213,7 @@ class Locker * * @return bool */ - public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags) + public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable) { $lock = array( '_readme' => array('This file locks the dependencies of your project to a known state', @@ -218,6 +225,7 @@ class Locker 'aliases' => array(), 'minimum-stability' => $minimumStability, 'stability-flags' => $stabilityFlags, + 'prefer-stable' => $preferStable, ); foreach ($aliases as $package => $versions) { 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 fe7582b42..af4eed811 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -47,6 +47,7 @@ install --prefer-dist "stability-flags": { "a/a": 20 }, + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test index 65b3fe80b..88c3e8fa7 100644 --- a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test +++ b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test @@ -24,7 +24,8 @@ Requirements from the composer file are not installed if the lock file is presen "packages-dev": null, "aliases": [], "minimum-stability": "stable", - "stability-flags": [] + "stability-flags": [], + "prefer-stable": false } --RUN-- install diff --git a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test index e5ddacf20..298846609 100644 --- a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test @@ -32,7 +32,8 @@ Installing an old alias that doesn't exist anymore from a lock is possible "packages-dev": null, "aliases": [], "minimum-stability": "dev", - "stability-flags": [] + "stability-flags": [], + "prefer-stable": false } --RUN-- install diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test index fb618ebe3..f9fd5058a 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test @@ -35,6 +35,7 @@ Partial update from lock file should apply lock file and downgrade unstable pack "stability-flags": { "b/unstable": 15 }, + "prefer-stable": false, "platform": [], "platform-dev": [] } @@ -57,6 +58,7 @@ update c/uptodate "aliases": [], "minimum-stability": "stable", "stability-flags": [], + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test index 51368b861..5b904f9b5 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test @@ -35,6 +35,7 @@ Partial update from lock file should update everything to the state of the lock, "stability-flags": { "b/unstable": 15 }, + "prefer-stable": false, "platform": [], "platform-dev": [] } @@ -57,6 +58,7 @@ update b/unstable "aliases": [], "minimum-stability": "stable", "stability-flags": [], + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test index 146277d02..224e58f7d 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test @@ -42,6 +42,7 @@ update b/unstable "aliases": [], "minimum-stability": "stable", "stability-flags": [], + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test index ad9451c6f..0fc5fe301 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test @@ -38,7 +38,8 @@ Update aliased package does not mess up the lock file "packages-dev": null, "aliases": [], "minimum-stability": "dev", - "stability-flags": [] + "stability-flags": [], + "prefer-stable": false } --INSTALLED-- [ @@ -64,6 +65,7 @@ update "aliases": [], "minimum-stability": "dev", "stability-flags": [], + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test index d73b93557..3bc189015 100644 --- a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test @@ -31,7 +31,8 @@ Limited update takes rules from lock if available, and not from the installed re "packages-dev": null, "aliases": [], "minimum-stability": "stable", - "stability-flags": [] + "stability-flags": [], + "prefer-stable": false } --INSTALLED-- [ diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test index f5c4ccc24..bd94617bc 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test @@ -19,7 +19,8 @@ Installing locked dev packages should remove old dependencies "packages-dev": null, "aliases": [], "minimum-stability": "dev", - "stability-flags": [] + "stability-flags": [], + "prefer-stable": false } --INSTALLED-- [ diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test index 3eb701719..849296850 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test @@ -31,7 +31,8 @@ Updating a dev package for new reference updates the url and reference "packages-dev": null, "aliases": [], "minimum-stability": "dev", - "stability-flags": {"a/a":20} + "stability-flags": {"a/a":20}, + "prefer-stable": false } --INSTALLED-- [ @@ -57,6 +58,7 @@ update "aliases": [], "minimum-stability": "dev", "stability-flags": {"a/a":20}, + "prefer-stable": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index b530eaf41..7e1d2d3b1 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -134,9 +134,10 @@ class LockerTest extends \PHPUnit_Framework_TestCase 'stability-flags' => array(), 'platform' => array(), 'platform-dev' => array(), + 'prefer-stable' => false, )); - $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array()); + $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false); } public function testLockBadPackages() @@ -155,7 +156,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $this->setExpectedException('LogicException'); - $locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array()); + $locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false); } public function testIsFresh() From cfc632b019bf9f567bdef6ccc433644140457ffd Mon Sep 17 00:00:00 2001 From: Gareth Evans Date: Fri, 11 Jul 2014 12:31:51 +0100 Subject: [PATCH 316/638] Determine auth URLs more intelligently --- src/Composer/Util/Git.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 296aa548f..053261b7b 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -106,13 +106,26 @@ class Git preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) && strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false ) { + if (strpos($match[2], '@')) { + list($authParts, $match[2]) = explode('@', $match[2], 2); + } + // TODO this should use an auth manager class that prompts and stores in the config if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); } else { + $defaultUsername = null; + if (isset($authParts) && $authParts) { + if (strpos($authParts, ':')) { + list($defaultUsername,) = explode(':', $authParts); + } else { + $defaultUsername = $authParts; + } + } + $this->io->write($url.' requires Authentication'); $auth = array( - 'username' => $this->io->ask('Username: '), + 'username' => $this->io->ask('Username: ', $defaultUsername), 'password' => $this->io->askAndHideAnswer('Password: '), ); } From dc90397d1e00f02cc32cd56d042533e132142d54 Mon Sep 17 00:00:00 2001 From: Garth Kerr Date: Sat, 12 Jul 2014 15:05:06 -0400 Subject: [PATCH 317/638] Add blank line to output for FileDownloader. The VcsDownloader outputs a blank line between each dependency: https://github.com/composer/composer/blob/745dcbce3317f7119575c39cef2cb601f9c5ffcf/src/Composer/Downloader/VcsDownloader.php#L80 This write makes output consistent. --- src/Composer/Downloader/FileDownloader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 793ae6be0..82a256b7c 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -101,6 +101,8 @@ class FileDownloader implements DownloaderInterface } } } + + $this->io->write(''); } protected function doDownload(PackageInterface $package, $path, $url) From 8b5c00bff0e29d9093101dde1d89141d9f8b7528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 16 Jul 2014 15:17:38 +0200 Subject: [PATCH 318/638] Fix phpdoc --- src/Composer/Autoload/ClassLoader.php | 4 ++++ src/Composer/Config/JsonConfigSource.php | 1 + src/Composer/Console/Application.php | 1 + src/Composer/Downloader/ArchiveDownloader.php | 1 + src/Composer/Downloader/DownloadManager.php | 1 + src/Composer/Downloader/GitDownloader.php | 4 +++- src/Composer/EventDispatcher/EventDispatcher.php | 2 +- src/Composer/Factory.php | 8 ++++++++ src/Composer/Installer.php | 2 ++ src/Composer/Package/Package.php | 2 +- src/Composer/Package/Version/VersionParser.php | 4 ++-- src/Composer/Plugin/PluginManager.php | 4 ++++ src/Composer/Util/Filesystem.php | 6 ++++++ src/Composer/Util/RemoteFilesystem.php | 3 ++- src/Composer/Util/Svn.php | 1 + 15 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 88684c526..443364959 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -143,6 +143,8 @@ class ClassLoader * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-0 base directories * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException */ public function addPsr4($prefix, $paths, $prepend = false) { @@ -204,6 +206,8 @@ class ClassLoader * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException */ public function setPsr4($prefix, $paths) { diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 6ca613ea6..4d0a873f5 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -37,6 +37,7 @@ class JsonConfigSource implements ConfigSourceInterface * Constructor * * @param JsonFile $file + * @param bool $authConfig */ public function __construct(JsonFile $file, $authConfig = false) { diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index da5d4ff20..b46ceb738 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -129,6 +129,7 @@ class Application extends BaseApplication /** * @param InputInterface $input + * @return string * @throws \RuntimeException */ private function getNewWorkingDir(InputInterface $input) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 7c0a761c6..a454490f3 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -132,6 +132,7 @@ abstract class ArchiveDownloader extends FileDownloader * Returns the folder content, excluding dotfiles * * @param string $dir Directory + * @return \SplFileInfo[] */ private function getFolderContent($dir) { diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 26c5a5301..01ccc822b 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -164,6 +164,7 @@ class DownloadManager * @param bool $preferSource prefer installation from source * * @throws \InvalidArgumentException if package have no urls to download from + * @throws \RuntimeException */ public function download(PackageInterface $package, $targetDir, $preferSource = null) { diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index f44b1514e..695996b5c 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -204,8 +204,10 @@ class GitDownloader extends VcsDownloader * @param string $path * @param string $reference * @param string $branch - * @param DateTime $date + * @param \DateTime $date * @return null|string if a string is returned, it is the commit reference that was checked out if the original could not be found + * + * @throws \RuntimeException */ protected function updateToCommit($path, $reference, $branch, $date) { diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index fb607831d..98e959c92 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -73,7 +73,7 @@ class EventDispatcher * Dispatch a script event. * * @param string $eventName The constant in ScriptEvents - * @param Script\Event $event + * @param bool $devMode */ public function dispatchScript($eventName, $devMode = false) { diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 5c5bfd5cf..e495f05f6 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -38,6 +38,7 @@ class Factory { /** * @return string + * @throws \RuntimeException */ protected static function getHomeDir() { @@ -60,6 +61,8 @@ class Factory } /** + * @param string $home + * * @return string */ protected static function getCacheDir($home) @@ -82,6 +85,7 @@ class Factory } /** + * @param IOInterface|null $io * @return Config */ public static function createConfig(IOInterface $io = null) @@ -333,6 +337,7 @@ class Factory /** * @param Config $config * @param string $vendorDir + * @return Repository\InstalledFilesystemRepository|null */ protected function createGlobalRepository(Config $config, $vendorDir) { @@ -410,6 +415,9 @@ class Factory } /** + * @param Composer $composer + * @param IOInterface $io + * @param RepositoryInterface $globalRepository * @return Plugin\PluginManager */ protected function createPluginManager(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index fac713226..67892d11f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -152,6 +152,8 @@ class Installer * Run installation (or update) * * @return int 0 on success or a positive error code on failure + * + * @throws \Exception */ public function run() { diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 2662fd32a..0c98a0e56 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -495,7 +495,7 @@ class Package extends BasePackage /** * Set the dev autoload mapping * - * @param array $autoload Mapping of dev autoloading rules + * @param array $devAutoload Mapping of dev autoloading rules */ public function setDevAutoload(array $devAutoload) { diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index ee13b77a5..812ccf5ad 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -89,7 +89,7 @@ class VersionParser * @param string $version * @param string $fullVersion optional complete version string to give more context * @throws \UnexpectedValueException - * @return array + * @return string */ public function normalize($version, $fullVersion = null) { @@ -161,7 +161,7 @@ class VersionParser * Normalizes a branch name to be able to perform comparisons on it * * @param string $name - * @return array + * @return string */ public function normalizeBranch($name) { diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d7c3ae07a..0305d31df 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -103,6 +103,8 @@ class PluginManager * call this method as early as possible. * * @param RepositoryInterface $repo Repository to scan for plugins to install + * + * @throws \RuntimeException */ public function loadRepository(RepositoryInterface $repo) { @@ -186,6 +188,8 @@ class PluginManager * instead for BC * * @param PackageInterface $package + * + * @throws \UnexpectedValueException */ public function registerPackage(PackageInterface $package) { diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index d167de463..182004205 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -90,6 +90,8 @@ class Filesystem * * @param string $directory * @return bool + * + * @throws \RuntimeException */ public function removeDirectory($directory) { @@ -174,6 +176,8 @@ class Filesystem * * @param string $path * @return bool + * + * @throws \RuntimeException */ public function unlink($path) { @@ -198,6 +202,8 @@ class Filesystem * * @param string $path * @return bool + * + * @throws \RuntimeException */ public function rmdir($path) { diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 0eed1b223..ee15f9d50 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -43,6 +43,7 @@ class RemoteFilesystem * Constructor. * * @param IOInterface $io The IO instance + * @param Config $config The config * @param array $options The options */ public function __construct(IOInterface $io, Config $config = null, array $options = array()) @@ -76,7 +77,7 @@ class RemoteFilesystem * @param boolean $progress Display the progression * @param array $options Additional context options * - * @return string The content + * @return bool|string The content */ public function getContents($originUrl, $fileUrl, $progress = true, $options = array()) { diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index a2836075f..4ec13297f 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -66,6 +66,7 @@ class Svn /** * @param string $url * @param \Composer\IO\IOInterface $io + * @param Config $config * @param ProcessExecutor $process */ public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null) From d5294ef91b061064785f077db0fd88717495ef16 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Jul 2014 18:37:12 +0200 Subject: [PATCH 319/638] Update repo url from github and handle trailing slashes, refs composer/packagist#354 refs composer/packagist#423 --- src/Composer/Repository/Vcs/GitHubDriver.php | 12 ++++++++++-- .../Test/Repository/Vcs/GitHubDriverTest.php | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 5ab590aea..06b805714 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -46,7 +46,7 @@ class GitHubDriver extends VcsDriver */ public function initialize() { - preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match); + preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match); $this->owner = $match[3]; $this->repository = $match[4]; $this->originUrl = !empty($match[1]) ? $match[1] : $match[2]; @@ -61,6 +61,11 @@ class GitHubDriver extends VcsDriver $this->fetchRootIdentifier(); } + public function getRepositoryUrl() + { + return 'https://'.$this->originUrl.'/'.$this->owner.'/'.$this->repository; + } + /** * {@inheritDoc} */ @@ -250,7 +255,7 @@ class GitHubDriver extends VcsDriver */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git)?$#', $url, $matches)) { + if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $url, $matches)) { return false; } @@ -401,6 +406,9 @@ class GitHubDriver extends VcsDriver return; } + $this->owner = $repoData['owner']['login']; + $this->repository = $repoData['name']; + $this->isPrivate = !empty($repoData['private']); if (isset($repoData['default_branch'])) { $this->rootIdentifier = $repoData['default_branch']; diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index c0d55eeca..d20da87dc 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -91,7 +91,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $remoteFilesystem->expects($this->at(3)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) - ->will($this->returnValue('{"master_branch": "test_master", "private": true}')); + ->will($this->returnValue('{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}')); $configSource = $this->getMock('Composer\Config\ConfigSourceInterface'); $authConfigSource = $this->getMock('Composer\Config\ConfigSourceInterface'); @@ -138,7 +138,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $remoteFilesystem->expects($this->at(0)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) - ->will($this->returnValue('{"master_branch": "test_master"}')); + ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')); $repoConfig = array( 'url' => $repoUrl, @@ -181,7 +181,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $remoteFilesystem->expects($this->at(0)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) - ->will($this->returnValue('{"master_branch": "test_master"}')); + ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')); $remoteFilesystem->expects($this->at(1)) ->method('getContents') From 799876dd559782a125731f18018bcbd3fb68d54a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Jul 2014 19:19:13 +0200 Subject: [PATCH 320/638] Allow dashes in class names for HHVM support, fixes #2967 --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 6cb306bfb..e74ecd108 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -146,7 +146,7 @@ class ClassMapGenerator preg_match_all('{ (?: - \b(?])(?Pclass|interface'.$traits.') \s+ (?P[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:]*) + \b(?])(?Pclass|interface'.$traits.') \s+ (?P[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*) | \b(?])(?Pnamespace) (?P\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;] ) }ix', $contents, $matches); From 4bd748b4630d01e3c268a941f748d0c317b7a156 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Jul 2014 19:43:59 +0200 Subject: [PATCH 321/638] Fix BC issue from #3101 --- src/Composer/Installer.php | 12 +++++++++++- src/Composer/Package/Locker.php | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index eb4db08c4..0fb66553b 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -648,7 +648,17 @@ class Installer private function createPolicy() { - return new DefaultPolicy((!$this->update && $this->locker->isLocked()) ? $this->locker->getPreferStable() : $this->package->getPreferStable()); + $preferStable = null; + if (!$this->update && $this->locker->isLocked()) { + $preferStable = $this->locker->getPreferStable(); + } + // old lock file without prefer stable will return null + // so in this case we use the composer.json info + if (null === $preferStable) { + $preferStable = $this->package->getPreferStable(); + } + + return new DefaultPolicy($preferStable); } private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index bee955288..ab5d432e0 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -177,7 +177,7 @@ class Locker { $lockData = $this->getLockData(); - return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : false; + return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null; } public function getAliases() @@ -210,6 +210,7 @@ class Locker * @param array $aliases array of aliases * @param string $minimumStability * @param array $stabilityFlags + * @param bool $preferStable * * @return bool */ From e3f32a79f30e55737c6c75bcf3e688c7c5e1bd9f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 19 Jul 2014 22:21:46 +0200 Subject: [PATCH 322/638] Finalize and bring up to speed the remove command, refs #2479 --- doc/03-cli.md | 17 +++++--- src/Composer/Command/RemoveCommand.php | 60 +++++++++++++++----------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index b91196516..7d557a8d3 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -163,6 +163,7 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master * **--no-update:** Disables the automatic update of the dependencies. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. +* **--update-no-dev** Run the dependency update with the --no-dev option. * **--update-with-dependencies** Also update dependencies of the newly required packages. @@ -171,18 +172,20 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master The `remove` command removes packages from the `composer.json` file from the current directory. - $ php composer.phar remove +```sh +php composer.phar remove vendor/package vendor/package2 +``` After removing the requirements, the modified requirements will be uninstalled. ### Options -* **--dry-run:** If you want to run through an uninstallation without actually - uninstalling a package, you can use `--dry-run`. This will simulate the - uninstallation and show you what would happen. -* **--dev:** Add packages to `require-dev`. -* **--no-update:** Only remove the package from the composer.json file, but - won't remove the files or update the composer.lock +* **--dev:** Remove packages from `require-dev`. +* **--no-update:** Disables the automatic update of the dependencies. +* **--no-progress:** Removes the progress display that can mess with some + terminals or scripts which don't handle backspace characters. +* **--update-no-dev** Run the dependency update with the --no-dev option. +* **--update-with-dependencies** Also update dependencies of the removed packages. ## global diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 97e6adc0e..be110d650 100755 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -25,6 +25,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** * @author Pierre du Plessis + * @author Jordi Boggiano */ class RemoveCommand extends Command { @@ -34,11 +35,12 @@ class RemoveCommand extends Command ->setName('remove') ->setDescription('Removes a package from the require or require-dev') ->setDefinition(array( - new InputArgument('packages', InputArgument::IS_ARRAY, 'Packages that should be removed, if not provided all packages are.'), - new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section'), - new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), - new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.') + new InputArgument('packages', InputArgument::IS_ARRAY, 'Packages that should be removed.'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'), + new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), + new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), )) ->setHelp(<<remove command removes a package from the current @@ -53,54 +55,64 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $composer = $this->getComposer(); $packages = $input->getArgument('packages'); - $io = $this->getIO(); - - $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); - $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - $file = Factory::getComposerFile(); $json = new JsonFile($file); + $composer = $json->read(); $composerBackup = file_get_contents($json->getPath()); $json = new JsonConfigSource($json); $type = $input->getOption('dev') ? 'require-dev' : 'require'; + $altType = !$input->getOption('dev') ? 'require-dev' : 'require'; foreach ($packages as $package) { - $json->removeLink($type, $package); + if (isset($composer[$type][$package])) { + $json->removeLink($type, $package); + } elseif (isset($composer[$altType][$package])) { + $output->writeln(''.$package.' could not be found in '.$type.' but it is present in '.$altType.''); + $dialog = $this->getHelperSet()->get('dialog'); + if ($this->getIO()->isInteractive()) { + if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) { + $json->removeLink($altType, $package); + } + } + } else { + $output->writeln(''.$package.' is not required in your composer.json and has not been removed'); + } } if ($input->getOption('no-update')) { - if ($input->getOption('dry-run')) { - file_put_contents($json->getPath(), $composerBackup); - } - return 0; } - $composer = Factory::create($io); + // Update packages + $composer = $this->getComposer(); + $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + $io = $this->getIO(); + + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); + $updateDevMode = !$input->getOption('update-no-dev'); $install - ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) - ->setDevMode($input->getOption('dev')) + ->setDevMode($updateDevMode) ->setUpdate(true) ->setUpdateWhitelist($packages) + ->setWhitelistDependencies($input->getOption('update-with-dependencies')); ; - if (!$install->run()) { - $output->writeln("\n".'Remove failed, reverting '.$file.' to its original content.'); + $status = $install->run(); + if ($status !== 0) { + $output->writeln("\n".'Removal failed, reverting '.$file.' to its original content.'); file_put_contents($json->getPath(), $composerBackup); - - return 1; } - return 0; + return $status; } } From a227523c4f61db01aaef3d1830d1e46422290d42 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 00:07:58 +0200 Subject: [PATCH 323/638] Fix php_strip_whitespace bugginess, fixes #3030 --- src/Composer/Autoload/ClassMapGenerator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e74ecd108..8f45adee8 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -115,7 +115,7 @@ class ClassMapGenerator $traits = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait'; try { - $contents = php_strip_whitespace($path); + $contents = @php_strip_whitespace($path); } catch (\Exception $e) { throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e); } @@ -160,8 +160,8 @@ class ClassMapGenerator } else { $name = $matches['name'][$i]; if ($name[0] === ':') { - // This is an XHP class, https://github.com/facebook/xhp - $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); + // This is an XHP class, https://github.com/facebook/xhp + $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); } $classes[] = ltrim($namespace . $name, '\\'); } From c03bcf01038d8b4b8d3609e140eb05af254a10fe Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 20 Jul 2014 16:44:30 +0200 Subject: [PATCH 324/638] Update SPDX license identifiers to version 1.19 --- res/spdx-identifier.json | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/res/spdx-identifier.json b/res/spdx-identifier.json index 8e472637e..b90531711 100644 --- a/res/spdx-identifier.json +++ b/res/spdx-identifier.json @@ -1,31 +1,32 @@ [ "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", "APL-1.0", "Aladdin", "ANTLR-PD", "Apache-1.0", "Apache-1.1", "Apache-2.0", "APSL-1.0", - "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0", "Artistic-2.0", "AAL", - "BitTorrent-1.0", "BitTorrent-1.1", "BSL-1.0", "BSD-3-Clause-Clear", - "BSD-2-Clause", "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", - "BSD-3-Clause", "BSD-4-Clause", "BSD-4-Clause-UC", "CECILL-1.0", - "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C", "ClArtistic", - "CNRI-Python", "CNRI-Python-GPL-Compatible", "CDDL-1.0", "CDDL-1.1", - "CPAL-1.0", "CPL-1.0", "CATOSL-1.1", "Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", - "CC-BY-2.5", "CC-BY-3.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", - "CC-BY-ND-3.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", - "CC-BY-NC-3.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", - "CC-BY-NC-ND-3.0", "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", - "CC-BY-NC-SA-3.0", "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", - "CC-BY-SA-3.0", "CC0-1.0", "CUA-OPL-1.0", "WTFPL", "EPL-1.0", "eCos-2.0", - "ECL-1.0", "ECL-2.0", "EFL-1.0", "EFL-2.0", "Entessa", "ErlPL-1.1", - "EUDatagrid", "EUPL-1.0", "EUPL-1.1", "Fair", "Frameworx-1.0", "FTL", + "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0", "Artistic-1.0-cl8", + "Artistic-1.0-Perl", "Artistic-2.0", "AAL", "BitTorrent-1.0", + "BitTorrent-1.1", "BSL-1.0", "BSD-2-Clause", "BSD-2-Clause-FreeBSD", + "BSD-2-Clause-NetBSD", "BSD-3-Clause", "BSD-3-Clause-Clear", "BSD-4-Clause", + "BSD-4-Clause-UC", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", + "CECILL-C", "ClArtistic", "CNRI-Python", "CNRI-Python-GPL-Compatible", + "CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1", + "Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0", + "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0", + "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", "CC-BY-NC-3.0", + "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", + "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", + "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0", "CC0-1.0", + "CUA-OPL-1.0", "D-FSL-1.0", "WTFPL", "EPL-1.0", "eCos-2.0", "ECL-1.0", + "ECL-2.0", "EFL-1.0", "EFL-2.0", "Entessa", "ErlPL-1.1", "EUDatagrid", + "EUPL-1.0", "EUPL-1.1", "Fair", "Frameworx-1.0", "FTL", "AGPL-1.0", "AGPL-3.0", "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0", "GPL-2.0+", "GPL-2.0-with-autoconf-exception", "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception", "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0", "GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception", "LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+", - "gSOAP-1.3b", "HPND", "IPL-1.0", "Imlib2", "IJG", "Intel", "IPA", "ISC", - "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", "LPPL-1.3c", - "Libpng", "LPL-1.02", "LPL-1.0", "MS-PL", "MS-RL", "MirOS", "MIT", - "Motosoto", "MPL-1.0", "MPL-1.1", "MPL-2.0", + "gSOAP-1.3b", "HPND", "IBM-pibs", "IPL-1.0", "Imlib2", "IJG", "Intel", + "IPA", "ISC", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", + "LPPL-1.3c", "Libpng", "LPL-1.02", "LPL-1.0", "MS-PL", "MS-RL", "MirOS", + "MIT", "Motosoto", "MPL-1.0", "MPL-1.1", "MPL-2.0", "MPL-2.0-no-copyleft-exception", "Multics", "NASA-1.3", "Naumen", "NBPL-1.0", "NGPL", "NOSL", "NPL-1.0", "NPL-1.1", "Nokia", "NPOSL-3.0", "NTP", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2", @@ -33,10 +34,11 @@ "OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3", "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OPL-1.0", "OSL-1.0", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8", "OpenSSL", "PHP-3.0", - "PHP-3.01", "PostgreSQL", "Python-2.0", "QPL-1.0", "RPSL-1.0", "RPL-1.5", - "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "SGI-B-1.0", "SGI-B-1.1", - "SGI-B-2.0", "OFL-1.0", "OFL-1.1", "SimPL-2.0", "Sleepycat", "SMLNJ", - "SugarCRM-1.1.3", "SISSL", "SPL-1.0", "Watcom-1.0", "NCSA", "VSL-1.0", - "W3C", "WXwindows", "Xnet", "X11", "XFree86-1.1", "YPL-1.0", "YPL-1.1", - "Zimbra-1.3", "Zlib", "ZPL-1.1", "ZPL-2.0", "ZPL-2.1" + "PHP-3.01", "PostgreSQL", "Python-2.0", "QPL-1.0", "RPSL-1.0", "RPL-1.1", + "RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "SGI-B-1.0", + "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1", "SimPL-2.0", "Sleepycat", + "SMLNJ", "SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", + "NCSA", "VSL-1.0", "W3C", "WXwindows", "Xnet", "X11", "XFree86-1.1", + "YPL-1.0", "YPL-1.1", "Zimbra-1.3", "Zlib", "ZPL-1.1", "ZPL-2.0", "ZPL-2.1", + "Unlicense" ] \ No newline at end of file From 22afc074a96d27b5462d335e22da893570bf7e87 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 18:46:44 +0200 Subject: [PATCH 325/638] Add browse command to docs, add a -H flag and tweak fallback mechanism to maximize chances of opening something, refs #2445 --- doc/03-cli.md | 10 ++++++++++ src/Composer/Command/HomeCommand.php | 30 ++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7d557a8d3..7a897fd37 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -268,6 +268,16 @@ php composer.phar show monolog/monolog 1.0.2 * **--platform (-p):** List only platform packages (php & extensions). * **--self (-s):** List the root package info. +## browse / home + +The `browse` (aliased to `home`) opens a package's repository URL or homepage +in your browser. + +### Options + +* **--homepage (-H):** Open the homepage instead of the repository URL. + times. + ## depends The `depends` command tells you which other packages depend on a certain diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 550f06308..1d3411a04 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -19,6 +19,7 @@ use Composer\Package\Loader\InvalidPackageException; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryInterface; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\InvalidArgumentException; @@ -36,12 +37,16 @@ class HomeCommand extends Command $this ->setName('browse') ->setAliases(array('home')) - ->setDescription('opens the package in your browser') + ->setDescription('Opens the package\'s repository URL or homepage in your browser.') ->setDefinition(array( - new InputArgument('package', InputArgument::REQUIRED, 'Package to goto'), + new InputArgument('package', InputArgument::REQUIRED, 'Package to browse to.'), + new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'), )) ->setHelp(<<getPackage($repo, $input->getArgument('package')); if (!$package instanceof CompletePackageInterface) { - throw new InvalidArgumentException('package not found'); + throw new InvalidArgumentException('Package not found'); } - if (filter_var($package->getSourceUrl(), FILTER_VALIDATE_URL)) { - $support = $package->getSupport(); - $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); - $this->openBrowser($url); - } else { - throw new InvalidPackageException(array($package->getName() => 'invalid source-url')); + + $support = $package->getSupport(); + $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); + if (!$url || $input->getOption('home')) { + $url = $package->getHomepage(); } + + if (!filter_var($url, FILTER_VALIDATE_URL)) { + throw new InvalidPackageException(array($package->getName() => $input->getOption('home') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL')); + } + + $this->openBrowser($url); } /** From fb5d01d635bbee98041d437d752acad21a14a5b2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 18:47:10 +0200 Subject: [PATCH 326/638] Tweak some page titles --- doc/03-cli.md | 2 +- doc/04-schema.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7a897fd37..ea0708ae5 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -1,4 +1,4 @@ -# Command-line interface +# Command-line interface / Commands You've already learned how to use the command-line interface to do some things. This chapter documents all the available commands. diff --git a/doc/04-schema.md b/doc/04-schema.md index e0db3c9af..2ebff5a30 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -1,4 +1,4 @@ -# composer.json +# The composer.json Schema This chapter will explain all of the fields available in `composer.json`. From b6ef95ecb846d0ef4c6e862dfddfe9d96434b413 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 18:54:16 +0200 Subject: [PATCH 327/638] Add support for opening multiple packages at once, refs #2445 --- src/Composer/Command/HomeCommand.php | 42 ++++++++++++++++++---------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 1d3411a04..8bf68cac6 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -39,7 +39,7 @@ class HomeCommand extends Command ->setAliases(array('home')) ->setDescription('Opens the package\'s repository URL or homepage in your browser.') ->setDefinition(array( - new InputArgument('package', InputArgument::REQUIRED, 'Package to browse to.'), + new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Package(s) to browse to.'), new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'), )) ->setHelp(<<initializeRepo($input, $output); - $package = $this->getPackage($repo, $input->getArgument('package')); + $return = 0; - if (!$package instanceof CompletePackageInterface) { - throw new InvalidArgumentException('Package not found'); + foreach ($input->getArgument('packages') as $packageName) { + $package = $this->getPackage($repo, $packageName); + + if (!$package instanceof CompletePackageInterface) { + $return = 1; + $output->writeln('Package '.$packageName.' not found'); + + continue; + } + + $support = $package->getSupport(); + $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); + if (!$url || $input->getOption('homepage')) { + $url = $package->getHomepage(); + } + + if (!filter_var($url, FILTER_VALIDATE_URL)) { + $return = 1; + $output->writeln(''.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.''); + + continue; + } + + $this->openBrowser($url); } - $support = $package->getSupport(); - $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl(); - if (!$url || $input->getOption('home')) { - $url = $package->getHomepage(); - } - - if (!filter_var($url, FILTER_VALIDATE_URL)) { - throw new InvalidPackageException(array($package->getName() => $input->getOption('home') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL')); - } - - $this->openBrowser($url); + return $return; } /** From 4dfa3c469e04e5db02e4840ea23a1aa6264f0f9c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 19:07:31 +0200 Subject: [PATCH 328/638] Fix class map generator handling of missing files --- src/Composer/Autoload/ClassMapGenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 8f45adee8..0f289bcbd 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -116,6 +116,14 @@ class ClassMapGenerator try { $contents = @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'); + } + } } catch (\Exception $e) { throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e); } From d9d8d09fe10497d0d688a1cbd24815fe55239529 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 19:46:51 +0200 Subject: [PATCH 329/638] Add support for local binaries in scripts, refs #2516 --- src/Composer/Command/RunScriptCommand.php | 19 ++++--- .../EventDispatcher/EventDispatcher.php | 49 ++++++++++++------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 527d8693d..50449b89d 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -12,6 +12,7 @@ namespace Composer\Command; +use Composer\Script\CommandEvent; use Composer\Script\ScriptEvents; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -75,16 +76,22 @@ EOT } } - $hasListeners = $this->getComposer()->getEventDispatcher()->hasEventListeners(new \Composer\Script\CommandEvent($script, $this->getComposer(), $this->getIO())); + $composer = $this->getComposer(); + $hasListeners = $composer->getEventDispatcher()->hasEventListeners(new CommandEvent($script, $composer, $this->getIO())); + if (!$hasListeners) { + throw new \InvalidArgumentException(sprintf('Script "%s" is not defined in this package', $script)); + } - if(!$hasListeners) { - throw new \InvalidArgumentException(sprintf('Script "%s" does not exist', $script)); + // add the bin dir to the PATH to make local binaries of deps usable in scripts + $binDir = $composer->getConfig()->get('bin-dir'); + if (is_dir($binDir)) { + putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); } if (in_array($script, $this->commandEvents)) { - $this->getComposer()->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); - } else { - $this->getComposer()->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev')); + return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); } + + return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev')); } } diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 3b116961b..c1d3be064 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -57,8 +57,10 @@ class EventDispatcher /** * Dispatch an event * - * @param string $eventName An event name - * @param Event $event + * @param string $eventName An event name + * @param Event $event + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 */ public function dispatch($eventName, Event $event = null) { @@ -66,47 +68,55 @@ class EventDispatcher $event = new Event($eventName); } - $this->doDispatch($event); + return $this->doDispatch($event); } /** * Dispatch a script event. * - * @param string $eventName The constant in ScriptEvents - * @param Script\Event $event + * @param string $eventName The constant in ScriptEvents + * @param Script\Event $event + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 */ public function dispatchScript($eventName, $devMode = false) { - $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode)); } /** * Dispatch a package event. * - * @param string $eventName The constant in ScriptEvents - * @param boolean $devMode Whether or not we are in dev mode - * @param OperationInterface $operation The package being installed/updated/removed + * @param string $eventName The constant in ScriptEvents + * @param boolean $devMode Whether or not we are in dev mode + * @param OperationInterface $operation The package being installed/updated/removed + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 */ public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation) { - $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation)); + return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation)); } /** * Dispatch a command event. * - * @param string $eventName The constant in ScriptEvents - * @param boolean $devMode Whether or not we are in dev mode + * @param string $eventName The constant in ScriptEvents + * @param boolean $devMode Whether or not we are in dev mode + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 */ public function dispatchCommandEvent($eventName, $devMode) { - $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode)); } /** * Triggers the listeners of an event. * * @param Event $event The event object to pass to the event handlers/listeners. + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 * @throws \RuntimeException * @throws \Exception */ @@ -114,9 +124,10 @@ class EventDispatcher { $listeners = $this->getListeners($event); + $return = 0; foreach ($listeners as $callable) { if (!is_string($callable) && is_callable($callable)) { - call_user_func($callable, $event); + $return = false === call_user_func($callable, $event) ? 1 : 0; } elseif ($this->isPhpScript($callable)) { $className = substr($callable, 0, strpos($callable, '::')); $methodName = substr($callable, strpos($callable, '::') + 2); @@ -131,7 +142,7 @@ class EventDispatcher } try { - $this->executeEventPhpScript($className, $methodName, $event); + $return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0; } catch (\Exception $e) { $message = "Script %s handling the %s event terminated with an exception"; $this->io->write(''.sprintf($message, $callable, $event->getName()).''); @@ -149,6 +160,8 @@ class EventDispatcher break; } } + + return $return; } /** @@ -158,7 +171,7 @@ class EventDispatcher */ protected function executeEventPhpScript($className, $methodName, Event $event) { - $className::$methodName($event); + return $className::$methodName($event); } /** @@ -219,14 +232,14 @@ class EventDispatcher /** * Checks if an event has listeners registered * - * @param Event $event + * @param Event $event * @return boolean */ public function hasEventListeners(Event $event) { $listeners = $this->getListeners($event); - return (sizeof($listeners) > 0); + return count($listeners) > 0; } /** From de559437eade9d8b41e38ef5708bdcd763f7ad48 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 19:49:28 +0200 Subject: [PATCH 330/638] Update deps --- composer.json | 2 +- composer.lock | 117 +++++++++++++++++++++++++------------------------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/composer.json b/composer.json index ea526e9c7..961ddf0ec 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "symfony/process": "~2.1" }, "require-dev": { - "phpunit/phpunit": "~3.7.10" + "phpunit/phpunit": "~3.7" }, "suggest": { "ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic", diff --git a/composer.lock b/composer.lock index f021ae47f..b50204a5d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e68bf60f228ca192b8b492cb95a80fa7", + "hash": "93e26fa183857a1712a2dc26d8506a12", "packages": [ { "name": "justinrainbow/json-schema", @@ -76,16 +76,16 @@ }, { "name": "seld/jsonlint", - "version": "1.1.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "7cd4c4965e17e6e4c07f26d566619a4c76f8c672" + "reference": "9cae56dbe34f4392e7d0f559474df33749a39f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/7cd4c4965e17e6e4c07f26d566619a4c76f8c672", - "reference": "7cd4c4965e17e6e4c07f26d566619a4c76f8c672", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9cae56dbe34f4392e7d0f559474df33749a39f8d", + "reference": "9cae56dbe34f4392e7d0f559474df33749a39f8d", "shasum": "" }, "require": { @@ -96,8 +96,8 @@ ], "type": "library", "autoload": { - "psr-0": { - "Seld\\JsonLint": "src/" + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", @@ -108,8 +108,7 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be", - "role": "Developer" + "homepage": "http://seld.be" } ], "description": "JSON Linter", @@ -119,36 +118,38 @@ "parser", "validator" ], - "time": "2013-11-04 15:41:11" + "time": "2014-07-20 17:36:11" }, { "name": "symfony/console", - "version": "v2.4.2", + "version": "v2.5.2", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe" + "reference": "386fa63407805959bd2c5fe540294721ad4224c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/940f217cbc3c8a33e5403e7c595495c4884400fe", - "reference": "940f217cbc3c8a33e5403e7c595495c4884400fe", + "url": "https://api.github.com/repos/symfony/Console/zipball/386fa63407805959bd2c5fe540294721ad4224c8", + "reference": "386fa63407805959bd2c5fe540294721ad4224c8", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { + "psr/log": "~1.0", "symfony/event-dispatcher": "~2.1" }, "suggest": { + "psr/log": "For using the console logger", "symfony/event-dispatcher": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -174,21 +175,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-02-11 13:52:09" + "time": "2014-07-15 14:15:12" }, { "name": "symfony/finder", - "version": "v2.4.2", + "version": "v2.5.2", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", "url": "https://github.com/symfony/Finder.git", - "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011" + "reference": "576d8f69feec477067e91b6bd0367c113e76a1a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/b6735d1fc16da13c4c7dddfe78366a4a098cf011", - "reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011", + "url": "https://api.github.com/repos/symfony/Finder/zipball/576d8f69feec477067e91b6bd0367c113e76a1a0", + "reference": "576d8f69feec477067e91b6bd0367c113e76a1a0", "shasum": "" }, "require": { @@ -197,7 +198,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -223,21 +224,21 @@ ], "description": "Symfony Finder Component", "homepage": "http://symfony.com", - "time": "2014-01-07 13:28:54" + "time": "2014-07-15 14:15:12" }, { "name": "symfony/process", - "version": "v2.4.2", + "version": "v2.5.2", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "c175448bac997556f8ab972908a4e14c7291fb03" + "reference": "5e53efbf61a7fbf73c79e3e08feea50f64c20bfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/c175448bac997556f8ab972908a4e14c7291fb03", - "reference": "c175448bac997556f8ab972908a4e14c7291fb03", + "url": "https://api.github.com/repos/symfony/Process/zipball/5e53efbf61a7fbf73c79e3e08feea50f64c20bfa", + "reference": "5e53efbf61a7fbf73c79e3e08feea50f64c20bfa", "shasum": "" }, "require": { @@ -246,7 +247,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -272,22 +273,22 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-02-11 13:52:09" + "time": "2014-07-09 09:05:48" } ], "packages-dev": [ { "name": "phpunit/php-code-coverage", - "version": "1.2.16", + "version": "1.2.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "69e55e68481cf708a6db43aff0b504e31402fe27" + "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/69e55e68481cf708a6db43aff0b504e31402fe27", - "reference": "69e55e68481cf708a6db43aff0b504e31402fe27", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", + "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", "shasum": "" }, "require": { @@ -335,7 +336,7 @@ "testing", "xunit" ], - "time": "2014-02-25 03:34:05" + "time": "2014-03-28 10:53:45" }, { "name": "phpunit/php-file-iterator", @@ -472,16 +473,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", "shasum": "" }, "require": { @@ -518,43 +519,42 @@ "keywords": [ "tokenizer" ], - "time": "2013-09-13 04:58:23" + "time": "2014-03-03 05:10:30" }, { "name": "phpunit/phpunit", - "version": "3.7.32", + "version": "3.7.37", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2752cbb9ea5bd84c2811b34b6953f76965ec7a2f" + "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2752cbb9ea5bd84c2811b34b6953f76965ec7a2f", - "reference": "2752cbb9ea5bd84c2811b34b6953f76965ec7a2f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", + "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", "shasum": "" }, "require": { + "ext-ctype": "*", "ext-dom": "*", + "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2.1", - "phpunit/php-file-iterator": ">=1.3.1", - "phpunit/php-text-template": ">=1.1.1", - "phpunit/php-timer": ">=1.0.4", - "phpunit/phpunit-mock-objects": "~1.2.0", + "phpunit/php-code-coverage": "~1.2", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.1", + "phpunit/php-timer": "~1.0", + "phpunit/phpunit-mock-objects": "~1.2", "symfony/yaml": "~2.0" }, "require-dev": { "pear-pear.php.net/pear": "1.9.4" }, "suggest": { - "ext-json": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "phpunit/php-invoker": ">=1.1.0,<1.2.0" + "phpunit/php-invoker": "~1.1" }, "bin": [ "composer/bin/phpunit" @@ -592,7 +592,7 @@ "testing", "xunit" ], - "time": "2014-02-25 03:47:29" + "time": "2014-04-30 12:24:19" }, { "name": "phpunit/phpunit-mock-objects", @@ -645,17 +645,17 @@ }, { "name": "symfony/yaml", - "version": "v2.4.2", + "version": "v2.5.2", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3" + "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/bb6ddaf8956139d1b8c360b4b713ed0138e876b3", - "reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", + "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", "shasum": "" }, "require": { @@ -664,7 +664,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -690,7 +690,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-01-07 13:28:54" + "time": "2014-07-09 09:05:48" } ], "aliases": [ @@ -700,6 +700,7 @@ "stability-flags": [ ], + "prefer-stable": false, "platform": { "php": ">=5.3.2" }, From cec1627acf7ea66799ec43116b460fb52cf0ce75 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 21:55:24 +0200 Subject: [PATCH 331/638] Add top level aliases for nonstandard scripts and allow parameter passing, refs #2516 --- composer.json | 3 + src/Composer/Command/RunScriptCommand.php | 7 +- src/Composer/Command/ScriptAliasCommand.php | 67 +++++++++++++++++++ src/Composer/Console/Application.php | 9 +++ src/Composer/EventDispatcher/Event.php | 21 +++++- .../EventDispatcher/EventDispatcher.php | 14 ++-- src/Composer/Plugin/CommandEvent.php | 5 +- src/Composer/Script/Event.php | 8 ++- 8 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 src/Composer/Command/ScriptAliasCommand.php diff --git a/composer.json b/composer.json index 961ddf0ec..718b06780 100644 --- a/composer.json +++ b/composer.json @@ -47,5 +47,8 @@ "branch-alias": { "dev-master": "1.0-dev" } + }, + "scripts": { + "test": "phpunit" } } diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 50449b89d..7f94814a7 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -55,6 +55,7 @@ class RunScriptCommand extends Command ->setDescription('Run the scripts defined in composer.json.') ->setDefinition(array( new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'), )) @@ -88,10 +89,12 @@ EOT putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); } + $args = $input->getArguments(); + if (in_array($script, $this->commandEvents)) { - return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); + return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); } - return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev')); + return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); } } diff --git a/src/Composer/Command/ScriptAliasCommand.php b/src/Composer/Command/ScriptAliasCommand.php new file mode 100644 index 000000000..958678068 --- /dev/null +++ b/src/Composer/Command/ScriptAliasCommand.php @@ -0,0 +1,67 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jordi Boggiano + */ +class ScriptAliasCommand extends Command +{ + private $script; + + public function __construct($script) + { + $this->script = $script; + + parent::__construct(); + } + + protected function configure() + { + $this + ->setName($this->script) + ->setDescription('Run the '.$this->script.' script as defined in composer.json.') + ->setDefinition(array( + new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), + )) + ->setHelp(<<run-script command runs scripts defined in composer.json: + +php composer.phar run-script post-update-cmd +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $composer = $this->getComposer(); + + // add the bin dir to the PATH to make local binaries of deps usable in scripts + $binDir = $composer->getConfig()->get('bin-dir'); + if (is_dir($binDir)) { + putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); + } + + $args = $input->getArguments(); + + return $composer->getEventDispatcher()->dispatchScript($this->script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 926d68719..29615af47 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -104,6 +104,15 @@ class Application extends BaseApplication $input->setInteractive(false); } + // add non-standard scripts as own commands + if ($composer = $this->getComposer(false)) { + foreach ($composer->getPackage()->getScripts() as $script => $dummy) { + if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { + $this->add(new Command\ScriptAliasCommand($script)); + } + } + } + if ($input->hasParameterOption('--profile')) { $startTime = microtime(true); $this->io->enableDebugging($startTime); diff --git a/src/Composer/EventDispatcher/Event.php b/src/Composer/EventDispatcher/Event.php index 0b3c9c951..7aff24f11 100644 --- a/src/Composer/EventDispatcher/Event.php +++ b/src/Composer/EventDispatcher/Event.php @@ -24,6 +24,11 @@ class Event */ protected $name; + /** + * @var array Arguments passed by the user + */ + protected $args; + /** * @var boolean Whether the event should not be passed to more listeners */ @@ -32,11 +37,13 @@ class Event /** * Constructor. * - * @param string $name The event name + * @param string $name The event name + * @param array $events Arguments passed by the user */ - public function __construct($name) + public function __construct($name, array $args = array()) { $this->name = $name; + $this->args = $args; } /** @@ -49,6 +56,16 @@ class Event return $this->name; } + /** + * Returns the event's arguments. + * + * @return array The event arguments + */ + public function getArguments() + { + return $this->args; + } + /** * Checks if stopPropagation has been called * diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index c1d3be064..5a7492610 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -76,12 +76,13 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param Script\Event $event + * @param array $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchScript($eventName, $devMode = false) + public function dispatchScript($eventName, $devMode = false, $additionalArgs = array()) { - return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); } /** @@ -103,18 +104,20 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param boolean $devMode Whether or not we are in dev mode + * @param array $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchCommandEvent($eventName, $devMode) + public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array()) { - return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); } /** * Triggers the listeners of an event. * * @param Event $event The event object to pass to the event handlers/listeners. + * @param string $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 * @throws \RuntimeException @@ -149,7 +152,8 @@ class EventDispatcher throw $e; } } else { - if (0 !== ($exitCode = $this->process->execute($callable))) { + $args = implode(' ', array_map('escapeshellarg', $event->getArguments())); + if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode); diff --git a/src/Composer/Plugin/CommandEvent.php b/src/Composer/Plugin/CommandEvent.php index 0f75bed9e..1c83db2b9 100644 --- a/src/Composer/Plugin/CommandEvent.php +++ b/src/Composer/Plugin/CommandEvent.php @@ -45,10 +45,11 @@ class CommandEvent extends Event * @param string $commandName The command name * @param InputInterface $input * @param OutputInterface $output + * @param array $events Arguments passed by the user */ - public function __construct($name, $commandName, $input, $output) + public function __construct($name, $commandName, $input, $output, array $args = array()) { - parent::__construct($name); + parent::__construct($name, $args); $this->commandName = $commandName; $this->input = $input; $this->output = $output; diff --git a/src/Composer/Script/Event.php b/src/Composer/Script/Event.php index 40b109b2d..58fb4788b 100644 --- a/src/Composer/Script/Event.php +++ b/src/Composer/Script/Event.php @@ -14,6 +14,7 @@ namespace Composer\Script; use Composer\Composer; use Composer\IO\IOInterface; +use Composer\EventDispatcher\Event as BaseEvent; /** * The script event class @@ -21,7 +22,7 @@ use Composer\IO\IOInterface; * @author François Pluchino * @author Nils Adermann */ -class Event extends \Composer\EventDispatcher\Event +class Event extends BaseEvent { /** * @var Composer The composer instance @@ -45,10 +46,11 @@ class Event extends \Composer\EventDispatcher\Event * @param Composer $composer The composer object * @param IOInterface $io The IOInterface object * @param boolean $devMode Whether or not we are in dev mode + * @param array $events Arguments passed by the user */ - public function __construct($name, Composer $composer, IOInterface $io, $devMode = false) + public function __construct($name, Composer $composer, IOInterface $io, $devMode = false, array $args = array()) { - parent::__construct($name); + parent::__construct($name, $args); $this->composer = $composer; $this->io = $io; $this->devMode = $devMode; From 11b945046fba36077ae9b5b6554cf4662ca2053f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Mon, 21 Jul 2014 15:07:27 +0200 Subject: [PATCH 332/638] Register plugin only one time when it's present in global and project mode --- src/Composer/Plugin/PluginManager.php | 6 ++++++ .../Test/Plugin/PluginInstallerTest.php | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d7c3ae07a..b6c88c90f 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -37,6 +37,7 @@ class PluginManager protected $versionParser; protected $plugins = array(); + protected $registeredPlugins = array(); private static $classCounter = 0; @@ -191,6 +192,10 @@ class PluginManager { $oldInstallerPlugin = ($package->getType() === 'composer-installer'); + if (in_array($package->getName(), $this->registeredPlugins)) { + return; + } + $extra = $package->getExtra(); if (empty($extra['class'])) { throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.'); @@ -233,6 +238,7 @@ class PluginManager } else { $plugin = new $class(); $this->addPlugin($plugin); + $this->registeredPlugins[] = $package->getName(); } } } diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index 08d0d1aab..bd9e2b554 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -164,4 +164,21 @@ class PluginInstallerTest extends \PHPUnit_Framework_TestCase $plugins = $this->pm->getPlugins(); $this->assertEquals('installer-v3', $plugins[1]->version); } + + public function testRegisterPluginOnlyOneTime() + { + $this->repository + ->expects($this->exactly(2)) + ->method('getPackages') + ->will($this->returnValue(array())); + $installer = new PluginInstaller($this->io, $this->composer); + $this->pm->loadInstalledPlugins(); + + $installer->install($this->repository, $this->packages[0]); + $installer->install($this->repository, clone $this->packages[0]); + + $plugins = $this->pm->getPlugins(); + $this->assertCount(1, $plugins); + $this->assertEquals('installer-v1', $plugins[0]->version); + } } From 67a016878ee2c576573a55b03b44a6fc3f3ba003 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 12:20:18 +0200 Subject: [PATCH 333/638] Switch working dir before loading composer, fixes #3146 --- src/Composer/Console/Application.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 29615af47..e967b0418 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -104,6 +104,12 @@ class Application extends BaseApplication $input->setInteractive(false); } + // switch working dir + if ($newWorkDir = $this->getNewWorkingDir($input)) { + $oldWorkingDir = getcwd(); + chdir($newWorkDir); + } + // add non-standard scripts as own commands if ($composer = $this->getComposer(false)) { foreach ($composer->getPackage()->getScripts() as $script => $dummy) { @@ -118,11 +124,6 @@ class Application extends BaseApplication $this->io->enableDebugging($startTime); } - if ($newWorkDir = $this->getNewWorkingDir($input)) { - $oldWorkingDir = getcwd(); - chdir($newWorkDir); - } - $result = parent::doRun($input, $output); if (isset($oldWorkingDir)) { From e5b36827ee1479f91dbd55ff24a75b71a1ffa6f1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 16:47:54 +0200 Subject: [PATCH 334/638] Improve scripts docs --- doc/articles/scripts.md | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index ccd8caf44..20d00133e 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -43,12 +43,12 @@ Composer fires the following named events during its execution process: - **pre-archive-cmd**: occurs before the `archive` command is executed. - **post-archive-cmd**: occurs after the `archive` command is executed. -**NOTE: Composer makes no assumptions about the state of your dependencies -prior to `install` or `update`. Therefore, you should not specify scripts that -require Composer-managed dependencies in the `pre-update-cmd` or -`pre-install-cmd` event hooks. If you need to execute scripts prior to -`install` or `update` please make sure they are self-contained within your -root package.** +> **Note:** Composer makes no assumptions about the state of your dependencies +> prior to `install` or `update`. Therefore, you should not specify scripts +> that require Composer-managed dependencies in the `pre-update-cmd` or +> `pre-install-cmd` event hooks. If you need to execute scripts prior to +> `install` or `update` please make sure they are self-contained within your +> root package. ## Defining scripts @@ -130,4 +130,29 @@ If you would like to run the scripts for an event manually, the syntax is: composer run-script [--dev] [--no-dev] script ``` -For example `composer run-script post-install-cmd` will run any **post-install-cmd** scripts that have been defined. +For example `composer run-script post-install-cmd` will run any +**post-install-cmd** scripts that have been defined. + +You can also give additional arguments to the script handler by appending `--` +followed by the handler arguments. e.g. +`composer run-script post-install-cmd -- --check` will pass`--check` along to +the script handler. Those arguments are received as CLI arg by CLI handlers, +and can be retrieved as an array via `$event->getArguments()` by PHP handlers. + +## Writing custom commands + +If you add custom scripts that do not fit one of the predefined event name +above, you can either run them with run-script or also run them as native +Composer commands. For example the handler defined below is executable by +simply running `composer test`: + +```json +{ + "scripts": { + "test": "phpunit" + } +} +``` + +> **Note:** Composer's bin-dir is pushed on top of the PATH so that binaries +> of dependencies are easily accessible as CLI commands when writing scripts. From 400f35efde99915585ae97c1eef7e1208e31e2fe Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 19:00:59 +0200 Subject: [PATCH 335/638] Doc tweak --- doc/articles/scripts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index 20d00133e..adbf07f6b 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -11,9 +11,9 @@ static method) or any command-line executable command. Scripts are useful for executing a package's custom code or package-specific commands during the Composer execution process. -**NOTE: Only scripts defined in the root package's `composer.json` are -executed. If a dependency of the root package specifies its own scripts, -Composer does not execute those additional scripts.** +> **Note:** Only scripts defined in the root package's `composer.json` are +> executed. If a dependency of the root package specifies its own scripts, +> Composer does not execute those additional scripts. ## Event names From 4f5d979beebe5646b878b203c6efda8889ed2129 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 19:01:22 +0200 Subject: [PATCH 336/638] ArchiveCommand improvements --- src/Composer/Command/ArchiveCommand.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index d7e627b34..34f6fe8a6 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -18,6 +18,9 @@ use Composer\DependencyResolver\Pool; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\CompositeRepository; use Composer\Script\ScriptEvents; +use Composer\Plugin\CommandEvent; +use Composer\Plugin\PluginEvents; +use Composer\Package\Version\VersionParser; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -38,7 +41,7 @@ class ArchiveCommand extends Command ->setDescription('Create an archive of this composer package') ->setDefinition(array( new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'), - new InputArgument('version', InputArgument::OPTIONAL, 'The package version to archive'), + new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip', 'tar'), new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory', '.'), )) @@ -56,7 +59,12 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $this->getComposer()->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD); + $composer = $this->getComposer(false); + if ($composer) { + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + $composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD); + } $returnCode = $this->archive( $this->getIO(), @@ -66,8 +74,8 @@ EOT $input->getOption('dir') ); - if (0 === $returnCode) { - $this->getComposer()->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD); + if (0 === $returnCode && $composer) { + $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD); } return $returnCode; @@ -112,16 +120,17 @@ EOT $pool = new Pool(); $pool->addRepository($repos); - $constraint = ($version) ? new VersionConstraint('>=', $version) : null; - $packages = $pool->whatProvides($packageName, $constraint); + $parser = new VersionParser(); + $constraint = ($version) ? $parser->parseConstraints($version) : null; + $packages = $pool->whatProvides($packageName, $constraint, true); if (count($packages) > 1) { - $package = $packages[0]; + $package = reset($packages); $io->write('Found multiple matches, selected '.$package->getPrettyString().'.'); $io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.'); $io->write('Please use a more specific constraint to pick a different package.'); } elseif ($packages) { - $package = $packages[0]; + $package = reset($packages); $io->write('Found an exact match '.$package->getPrettyString().'.'); } else { $io->write('Could not find a package matching '.$packageName.'.'); From 1110074d5fc1b6aec83392a4de5a948eb1beda23 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 19:02:01 +0200 Subject: [PATCH 337/638] Clean up properly after creating the vendor dir --- src/Composer/Downloader/ArchiveDownloader.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 7c0a761c6..884d2f126 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -64,6 +64,12 @@ abstract class ArchiveDownloader extends FileDownloader } $this->filesystem->removeDirectory($temporaryDir); + if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir').'/composer/')) { + $this->filesystem->removeDirectory($this->config->get('vendor-dir').'/composer/'); + } + if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir'))) { + $this->filesystem->removeDirectory($this->config->get('vendor-dir')); + } } catch (\Exception $e) { // clean up $this->filesystem->removeDirectory($path); From 904f2830e7e6eaf3a78124c10b71cf476325cd8c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 23 Jul 2014 19:03:55 +0200 Subject: [PATCH 338/638] Archive manager tweaks to reduce file path lengths, fixes #2808 --- src/Composer/Package/Archiver/ArchiveManager.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index a8891e0c7..55d87c1a3 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -14,7 +14,7 @@ namespace Composer\Package\Archiver; use Composer\Downloader\DownloadManager; use Composer\Package\PackageInterface; -use Composer\Package\RootPackage; +use Composer\Package\RootPackageInterface; use Composer\Util\Filesystem; use Composer\Json\JsonFile; @@ -133,11 +133,11 @@ class ArchiveManager return $target; } - if ($package instanceof RootPackage) { + if ($package instanceof RootPackageInterface) { $sourcePath = realpath('.'); } else { // Directory used to download the sources - $sourcePath = sys_get_temp_dir().'/composer_archiver/'.$packageName; + $sourcePath = sys_get_temp_dir().'/composer_archiver/arch'.uniqid(); $filesystem->ensureDirectoryExists($sourcePath); // Download sources @@ -154,13 +154,17 @@ class ArchiveManager } // Create the archive - $archivePath = $usableArchiver->archive($sourcePath, $target, $format, $package->getArchiveExcludes()); + $tempTarget = sys_get_temp_dir().'/composer_archiver/arch'.uniqid().'.'.$format; + $filesystem->ensureDirectoryExists(dirname($tempTarget)); + + $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes()); + rename($archivePath, $target); // cleanup temporary download - if (!$package instanceof RootPackage) { + if (!$package instanceof RootPackageInterface) { $filesystem->removeDirectory($sourcePath); } - return $archivePath; + return $target; } } From 3dc83277a38f2f6731fe2615885297181054892d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 23 Jul 2014 20:19:29 +0200 Subject: [PATCH 339/638] Fix phpdoc --- src/Composer/Repository/RepositoryManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 1a9054f14..83352cf2f 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -89,7 +89,7 @@ class RepositoryManager * Returns a new repository for a specific installation type. * * @param string $type repository type - * @param string $config repository configuration + * @param array $config repository configuration * @return RepositoryInterface * @throws \InvalidArgumentException if repository for provided type is not registered */ From 4ebc5c9a08ba3a4861e050463d5d40204b5cdedf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 24 Jul 2014 16:01:42 +0200 Subject: [PATCH 340/638] Add auth helper and reuse it in git downloader --- src/Composer/Util/AuthHelper.php | 63 ++++++++++++++++++++++++++ src/Composer/Util/Git.php | 26 ++++++----- src/Composer/Util/RemoteFilesystem.php | 31 +------------ 3 files changed, 80 insertions(+), 40 deletions(-) create mode 100644 src/Composer/Util/AuthHelper.php diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php new file mode 100644 index 000000000..4accba716 --- /dev/null +++ b/src/Composer/Util/AuthHelper.php @@ -0,0 +1,63 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Composer\Config; +use Composer\IO\IOInterface; + +/** + * @author Jordi Boggiano + */ +class AuthHelper +{ + protected $io; + protected $config; + + public function __construct(IOInterface $io, Config $config) + { + $this->io = $io; + $this->config = $config; + } + + public function storeAuth($originUrl, $storeAuth) + { + $store = false; + $configSource = $this->config->getAuthConfigSource(); + if ($storeAuth === true) { + $store = $configSource; + } elseif ($storeAuth === 'prompt') { + $answer = $this->io->askAndValidate( + 'Do you want to store credentials for '.$originUrl.' in '.$configSource->getName().' ? [Yn] ', + function ($value) { + $input = strtolower(substr(trim($value), 0, 1)); + if (in_array($input, array('y','n'))) { + return $input; + } + throw new \RuntimeException('Please answer (y)es or (n)o'); + }, + false, + 'y' + ); + + if ($answer === 'y') { + $store = $configSource; + } + } + if ($store) { + $store->addConfigSetting( + 'http-basic.'.$originUrl, + $this->io->getAuthentication($originUrl) + ); + } + } +} diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 296aa548f..3c60157e9 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -102,28 +102,32 @@ class Git } } } elseif ( // private non-github repo that failed to authenticate - $this->io->isInteractive() && preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) && strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false ) { - // TODO this should use an auth manager class that prompts and stores in the config + $storeAuth = false; if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); - } else { - $this->io->write($url.' requires Authentication'); + } elseif ($this->io->isInteractive()) { + $this->io->write(' Authentication required ('.parse_url($url, PHP_URL_HOST).'):'); $auth = array( - 'username' => $this->io->ask('Username: '), - 'password' => $this->io->askAndHideAnswer('Password: '), + 'username' => $this->io->ask(' Username: '), + 'password' => $this->io->askAndHideAnswer(' Password: '), ); + $storeAuth = $this->config->get('store-auths'); } - $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; + if ($auth) { + $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3]; - $command = call_user_func($commandCallable, $url); - if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { - $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); + $command = call_user_func($commandCallable, $url); + if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { + $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); + $authHelper = new AuthHelper($this->io, $this->config); + $authHelper->storeAuth($match[2], $storeAuth); - return; + return; + } } } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index ee15f9d50..780f437c6 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -253,35 +253,8 @@ class RemoteFilesystem $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); - $store = false; - $configSource = $this->config->getAuthConfigSource(); - if ($this->storeAuth === true) { - $store = $configSource; - } elseif ($this->storeAuth === 'prompt') { - $answer = $this->io->askAndValidate( - 'Do you want to store credentials for '.$this->originUrl.' in '.$configSource->getName().' ? [Yn] ', - function ($value) { - $input = strtolower(substr(trim($value), 0, 1)); - if (in_array($input, array('y','n'))) { - return $input; - } - throw new \RuntimeException('Please answer (y)es or (n)o'); - }, - false, - 'y' - ); - - if ($answer === 'y') { - $store = $configSource; - } - } - if ($store) { - $store->addConfigSetting( - 'http-basic.'.$this->originUrl, - $this->io->getAuthentication($this->originUrl) - ); - } - + $authHelper = new AuthHelper($this->io, $this->config); + $authHelper->storeAuth($this->originUrl, $this->storeAuth); $this->storeAuth = false; return $result; From bddba72e132cfd67576682bff709f535e5d912ea Mon Sep 17 00:00:00 2001 From: xelan Date: Fri, 25 Jul 2014 12:37:11 +0200 Subject: [PATCH 341/638] Option to disable inclusion of hostname in OAuth app name --- src/Composer/Config.php | 1 + src/Composer/Util/GitHub.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index d5b41efe2..e740bb8c7 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -39,6 +39,7 @@ class Config 'optimize-autoloader' => false, 'prepend-autoloader' => true, 'github-domains' => array('github.com'), + 'github-expose-hostname' => true, 'store-auths' => 'prompt', // valid keys without defaults (auth config stuff): // github-oauth diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 784cb51c9..8624edc04 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -97,8 +97,10 @@ class GitHub // build up OAuth app name $appName = 'Composer'; - if (0 === $this->process->execute('hostname', $output)) { + if ($this->config->get('github-expose-hostname') === true && 0 === $this->process->execute('hostname', $output)) { $appName .= ' on ' . trim($output); + } else { + $appName .= ' [' . date('YmdHis') . ']'; } $headers = array(); From 6f80b26c3263da6fd7f64a3bde76ddfdde70f424 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 25 Jul 2014 14:39:05 +0200 Subject: [PATCH 342/638] Add comment --- src/Composer/Package/Locker.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index ab5d432e0..2fa9b011a 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -177,6 +177,8 @@ class Locker { $lockData = $this->getLockData(); + // return null if not set to allow caller logic to choose the + // right behavior since old lock files have no prefer-stable return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null; } From 126495e3c8e0d0f1dc90f110d84755768c7dec34 Mon Sep 17 00:00:00 2001 From: Amy Boyd Date: Sat, 5 Jul 2014 13:53:31 +0100 Subject: [PATCH 343/638] Update troubeshooting.md to suggest clearing Composer's cache. --- doc/articles/troubleshooting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index 838b7611c..1db90b8d4 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -21,6 +21,8 @@ This is a list of common pitfalls on using Composer, and how to avoid them. possible interferences with existing vendor installations or `composer.lock` entries. +5. Try clearing Composer's cache - `rm -r ~/.composer/cache`. + ## Package not found 1. Double-check you **don't have typos** in your `composer.json` or repository @@ -38,6 +40,10 @@ This is a list of common pitfalls on using Composer, and how to avoid them. your repository, especially when maintaining a third party fork and using `replace`. +5. If you are updating to a recently published version of a package, be aware that + Packagist has a delay of up to 10 minutes before new packages are added to it's + index. + ## Package not found on travis-ci.org 1. Check the ["Package not found"](#package-not-found) item above. From ff844b3e14913da51455812f9e258715980d411d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jul 2014 12:59:19 +0200 Subject: [PATCH 344/638] Load scripts without preloading a composer instance to avoid side-effects, fixes #3155 --- src/Composer/Console/Application.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index b4fa7c5aa..4e2fa14b2 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -25,6 +25,7 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\IO\ConsoleIO; use Composer\Json\JsonValidationException; +use Composer\Json\JsonFile; use Composer\Util\ErrorHandler; /** @@ -111,10 +112,14 @@ class Application extends BaseApplication } // add non-standard scripts as own commands - if ($composer = $this->getComposer(false)) { - foreach ($composer->getPackage()->getScripts() as $script => $dummy) { - if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { - $this->add(new Command\ScriptAliasCommand($script)); + $file = Factory::getComposerFile(); + $json = new JsonFile($file); + if ($json->exists() && is_readable($file) && ($composer = $json->read())) { + if (isset($composer['scripts']) && is_array($composer['scripts'])) { + foreach ($composer['scripts'] as $script => $dummy) { + if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { + $this->add(new Command\ScriptAliasCommand($script)); + } } } } From f8c6b3c71bd708318a54748df4e59ae94928af57 Mon Sep 17 00:00:00 2001 From: Markus Poerschke Date: Mon, 28 Jul 2014 22:08:21 +0200 Subject: [PATCH 345/638] Fixed composer.lock hash --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index b50204a5d..e5c6b524c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "93e26fa183857a1712a2dc26d8506a12", + "hash": "eeb4afc3be46412ec15842ce4af01f0b", "packages": [ { "name": "justinrainbow/json-schema", From ed507dec9fa898cef3dfa80b779acc03eedfdecd Mon Sep 17 00:00:00 2001 From: hakre Date: Thu, 24 Jul 2014 14:17:42 +0200 Subject: [PATCH 346/638] added test unlinking directory #3157 --- tests/Composer/Test/Util/FilesystemTest.php | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/Composer/Test/Util/FilesystemTest.php b/tests/Composer/Test/Util/FilesystemTest.php index c74e84a5f..bffea7434 100644 --- a/tests/Composer/Test/Util/FilesystemTest.php +++ b/tests/Composer/Test/Util/FilesystemTest.php @@ -176,4 +176,31 @@ class FilesystemTest extends TestCase array('phar://c:../Foo', 'phar://c:../Foo'), ); } + + /** + * @link https://github.com/composer/composer/issues/3157 + */ + public function testUnlinkSymlinkedDirectory() + { + $tmp = sys_get_temp_dir(); + $basepath = $tmp . "/composer_testdir"; + $symlinked = $basepath . "/linked"; + @mkdir($basepath . "/real", 0777, true); + touch($basepath . "/real/FILE"); + + $result = @symlink($basepath . "/real", $symlinked); + + if (!$result) { + $this->markTestSkipped('Symbolic links for directories not supported on this platform'); + } + + if (!is_dir($symlinked)) { + $this->fail('Precondition assertion failed (is_dir is false on symbolic link to directory).'); + } + + $fs = new Filesystem(); + $result = $fs->unlink($symlinked); + $this->assertTrue($result); + $this->assertFalse(file_exists($symlinked)); + } } From 0ad2449fe8d7c6a0295626c285d55466847ecedb Mon Sep 17 00:00:00 2001 From: hakre Date: Mon, 28 Jul 2014 23:42:53 +0200 Subject: [PATCH 347/638] rmdir needs to be used on windows to remove symbolic links to directories --- src/Composer/Util/Filesystem.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 182004205..7cc463525 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -181,9 +181,9 @@ class Filesystem */ public function unlink($path) { - if (!@unlink($path)) { + 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) && !@unlink($path))) { + if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@$this->unlinkImplementation($path))) { $error = error_get_last(); $message = 'Could not delete '.$path.': ' . @$error['message']; if (defined('PHP_WINDOWS_VERSION_BUILD')) { @@ -473,4 +473,22 @@ class Filesystem { return new ProcessExecutor; } + + /** + * delete symbolic link implementation (commonly known as "unlink()") + * + * symbolic links on windows which link to directories need rmdir instead of unlink + * + * @param string $path + * + * @return bool + */ + private function unlinkImplementation($path) + { + if (defined('PHP_WINDOWS_VERSION_BUILD') && is_dir($path) && is_link($path)) { + return rmdir($path); + } + + return unlink($path); + } } From 343d0b5af2a9e7216ac7408d4cfc097742566770 Mon Sep 17 00:00:00 2001 From: hakre Date: Thu, 24 Jul 2014 14:42:37 +0200 Subject: [PATCH 348/638] added test removing directory with trailing slash that is symlinked #3144 #3157 --- tests/Composer/Test/Util/FilesystemTest.php | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Composer/Test/Util/FilesystemTest.php b/tests/Composer/Test/Util/FilesystemTest.php index bffea7434..a4d1255f9 100644 --- a/tests/Composer/Test/Util/FilesystemTest.php +++ b/tests/Composer/Test/Util/FilesystemTest.php @@ -203,4 +203,38 @@ class FilesystemTest extends TestCase $this->assertTrue($result); $this->assertFalse(file_exists($symlinked)); } + + /** + * @link https://github.com/composer/composer/issues/3144 + */ + public function testRemoveSymlinkedDirectoryWithTrailingSlash() + { + $tmp = sys_get_temp_dir(); + $basepath = $tmp . "/composer_testdir"; + @mkdir($basepath . "/real", 0777, true); + touch($basepath . "/real/FILE"); + $symlinked = $basepath . "/linked"; + $symlinkedTrailingSlash = $symlinked . "/"; + + $result = @symlink($basepath . "/real", $symlinked); + + if (!$result) { + $this->markTestSkipped('Symbolic links for directories not supported on this platform'); + } + + if (!is_dir($symlinked)) { + $this->fail('Precondition assertion failed (is_dir is false on symbolic link to directory).'); + } + + if (!is_dir($symlinkedTrailingSlash)) { + $this->fail('Precondition assertion failed (is_dir false w trailing slash).'); + } + + $fs = new Filesystem(); + + $result = $fs->removeDirectory($symlinkedTrailingSlash); + $this->assertTrue($result); + $this->assertFalse(file_exists($symlinkedTrailingSlash)); + $this->assertFalse(file_exists($symlinked)); + } } From 3e727850ffd1cdec4acdd483824b65c645c84d57 Mon Sep 17 00:00:00 2001 From: hakre Date: Thu, 24 Jul 2014 15:08:25 +0200 Subject: [PATCH 349/638] unlinking symlinked directories and trailing slashes Filesystem::removeDirectory() didn't detect all symlinked directories properly due to not resolving pathnames with trailing slashes first. this commit fixes that issue by resolving pathnames with trailing slashes by removing those in case they are representing a symlinked directory. #3144 Reference: A.4.12 Pathname Resolution - The Open Group Base Specifications Issue 7 IEEE Std 1003.1, 2013 Edition Section --- src/Composer/Util/Filesystem.php | 49 ++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 7cc463525..5104caaec 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -95,8 +95,8 @@ class Filesystem */ public function removeDirectory($directory) { - if (file_exists($directory) && is_link($directory)) { - return $this->unlink($directory); + if ($this->isSymlinkedDirectory($directory)) { + return $this->unlinkSymlinkedDirectory($directory); } if (!file_exists($directory) || !is_dir($directory)) { @@ -491,4 +491,49 @@ class Filesystem return unlink($path); } + + private function isSymlinkedDirectory($directory) + { + if (!is_dir($directory)) { + return false; + } + + $resolved = $this->resolveSymlinkedDirectorySymlink($directory); + + return is_link($resolved); + } + + /** + * @param string $directory + * + * @return bool + */ + private function unlinkSymlinkedDirectory($directory) + { + $resolved = $this->resolveSymlinkedDirectorySymlink($directory); + + return $this->unlink($resolved); + } + + /** + * resolve pathname to symbolic link of a directory + * + * @param string $pathname directory path to resolve + * + * @return string resolved path to symbolic link or original pathname (unresolved) + */ + private function resolveSymlinkedDirectorySymlink($pathname) + { + if (!is_dir($pathname)) { + return $pathname; + } + + $resolved = rtrim($pathname, '/'); + + if (!strlen($resolved)) { + return $pathname; + } + + return $resolved; + } } From 1067ce4f9623a7f20cbf4b88c5cf6f18609606d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 29 Jul 2014 15:25:16 +0200 Subject: [PATCH 350/638] Add installer events --- .../EventDispatcher/EventDispatcher.php | 24 +++ src/Composer/Installer.php | 5 + src/Composer/Installer/InstallerEvent.php | 146 ++++++++++++++++++ src/Composer/Installer/InstallerEvents.php | 43 ++++++ .../EventDispatcher/EventDispatcherTest.php | 25 +++ .../Test/Installer/InstallerEventTest.php | 39 +++++ 6 files changed, 282 insertions(+) create mode 100644 src/Composer/Installer/InstallerEvent.php create mode 100644 src/Composer/Installer/InstallerEvents.php create mode 100644 tests/Composer/Test/Installer/InstallerEventTest.php diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 7e882ddb3..d5dad92ca 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -12,9 +12,14 @@ namespace Composer\EventDispatcher; +use Composer\DependencyResolver\PolicyInterface; +use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\Request; +use Composer\Installer\InstallerEvent; use Composer\IO\IOInterface; use Composer\Composer; use Composer\DependencyResolver\Operation\OperationInterface; +use Composer\Repository\CompositeRepository; use Composer\Script; use Composer\Script\CommandEvent; use Composer\Script\PackageEvent; @@ -113,6 +118,25 @@ class EventDispatcher return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); } + + /** + * Dispatch a installer event. + * + * @param string $eventName The constant in InstallerEvents + * @param PolicyInterface $policy The policy + * @param Pool $pool The pool + * @param CompositeRepository $installedRepo The installed repository + * @param Request $request The request + * @param array $operations The list of operations + * + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 + */ + public function dispatchInstallerEvent($eventName, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + { + return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $policy, $pool, $installedRepo, $request, $operations)); + } + /** * Triggers the listeners of an event. * diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 3176ece17..a71dec8ac 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -26,6 +26,7 @@ use Composer\DependencyResolver\SolverProblemsException; use Composer\Downloader\DownloadManager; use Composer\EventDispatcher\EventDispatcher; use Composer\Installer\InstallationManager; +use Composer\Installer\InstallerEvents; use Composer\Installer\NoopInstaller; use Composer\IO\IOInterface; use Composer\Json\JsonFile; @@ -260,8 +261,10 @@ class Installer $request->install($link->getTarget(), $link->getConstraint()); } + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); $ops = $solver->solve($request); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request, $ops); foreach ($ops as $op) { if ($op->getJobType() === 'uninstall') { $devPackages[] = $op->getPackage(); @@ -464,9 +467,11 @@ class Installer $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links'); // solve dependencies + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); try { $operations = $solver->solve($request); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request, $operations); } catch (SolverProblemsException $e) { $this->io->write('Your requirements could not be resolved to an installable set of packages.'); $this->io->write($e->getMessage()); diff --git a/src/Composer/Installer/InstallerEvent.php b/src/Composer/Installer/InstallerEvent.php new file mode 100644 index 000000000..a9f5a728a --- /dev/null +++ b/src/Composer/Installer/InstallerEvent.php @@ -0,0 +1,146 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer; + +use Composer\Composer; +use Composer\DependencyResolver\PolicyInterface; +use Composer\DependencyResolver\Operation\OperationInterface; +use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\Request; +use Composer\EventDispatcher\Event; +use Composer\IO\IOInterface; +use Composer\Repository\CompositeRepository; + +/** + * An event for all installer. + * + * @author François Pluchino + */ +class InstallerEvent extends Event +{ + /** + * @var Composer + */ + private $composer; + + /** + * @var IOInterface + */ + private $io; + + /** + * @var PolicyInterface + */ + private $policy; + + /** + * @var Pool + */ + private $pool; + + /** + * @var CompositeRepository + */ + private $installedRepo; + + /** + * @var Request + */ + private $request; + + /** + * @var OperationInterface[] + */ + private $operations; + + /** + * Constructor. + * + * @param string $eventName + * @param Composer $composer + * @param IOInterface $io + * @param PolicyInterface $policy + * @param Pool $pool + * @param CompositeRepository $installedRepo + * @param Request $request + * @param OperationInterface[] $operations + */ + public function __construct($eventName, Composer $composer, IOInterface $io, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + { + parent::__construct($eventName); + + $this->composer = $composer; + $this->io = $io; + $this->policy = $policy; + $this->pool = $pool; + $this->installedRepo = $installedRepo; + $this->request = $request; + $this->operations = $operations; + } + + /** + * @return Composer + */ + public function getComposer() + { + return $this->composer; + } + + /** + * @return IOInterface + */ + public function getIO() + { + return $this->io; + } + + /** + * @return PolicyInterface + */ + public function getPolicy() + { + return $this->policy; + } + + /** + * @return Pool + */ + public function getPool() + { + return $this->pool; + } + + /** + * @return CompositeRepository + */ + public function getInstalledRepo() + { + return $this->installedRepo; + } + + /** + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @return OperationInterface[] + */ + public function getOperations() + { + return $this->operations; + } +} diff --git a/src/Composer/Installer/InstallerEvents.php b/src/Composer/Installer/InstallerEvents.php new file mode 100644 index 000000000..5929b0964 --- /dev/null +++ b/src/Composer/Installer/InstallerEvents.php @@ -0,0 +1,43 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer; + +/** + * The Installer Events. + * + * @author François Pluchino + */ +class InstallerEvents +{ + /** + * The PRE_SOLVE_DEPENDENCIES event occurs as a installer begins + * resolve operations. + * + * The event listener method receives a + * Composer\Installer\InstallerEvent instance. + * + * @var string + */ + const PRE_SOLVE_DEPENDENCIES = 'pre-solve-dependencies'; + + /** + * The POST_SOLVE_DEPENDENCIES event occurs as a installer after + * resolve operations. + * + * The event listener method receives a + * Composer\Installer\InstallerEvent instance. + * + * @var string + */ + const POST_SOLVE_DEPENDENCIES = 'post-solve-dependencies'; +} diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index fd26b0a3c..a6cad7602 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -174,6 +174,31 @@ class EventDispatcherTest extends TestCase $dispatcher->dispatchCommandEvent("post-install-cmd", false); } + public function testDispatcherInstallerEvents() + { + $process = $this->getMock('Composer\Util\ProcessExecutor'); + $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher') + ->setConstructorArgs(array( + $this->getMock('Composer\Composer'), + $this->getMock('Composer\IO\IOInterface'), + $process, + )) + ->setMethods(array('getListeners')) + ->getMock(); + + $dispatcher->expects($this->atLeastOnce()) + ->method('getListeners') + ->will($this->returnValue(array())); + + $policy = $this->getMock('Composer\DependencyResolver\PolicyInterface'); + $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock(); + $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock(); + $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock(); + + $dispatcher->dispatchInstallerEvent("pre-solve-dependencies", $policy, $pool, $installedRepo, $request); + $dispatcher->dispatchInstallerEvent("post-solve-dependencies", $policy, $pool, $installedRepo, $request, array()); + } + public static function call() { throw new \RuntimeException(); diff --git a/tests/Composer/Test/Installer/InstallerEventTest.php b/tests/Composer/Test/Installer/InstallerEventTest.php new file mode 100644 index 000000000..7cd63f6b5 --- /dev/null +++ b/tests/Composer/Test/Installer/InstallerEventTest.php @@ -0,0 +1,39 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Installer; + +use Composer\Installer\InstallerEvent; + +class InstallerEventTest extends \PHPUnit_Framework_TestCase +{ + public function testGetter() + { + $composer = $this->getMock('Composer\Composer'); + $io = $this->getMock('Composer\IO\IOInterface'); + $policy = $this->getMock('Composer\DependencyResolver\PolicyInterface'); + $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock(); + $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock(); + $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock(); + $operations = array($this->getMock('Composer\DependencyResolver\Operation\OperationInterface')); + $event = new InstallerEvent('EVENT_NAME', $composer, $io, $policy, $pool, $installedRepo, $request, $operations); + + $this->assertSame('EVENT_NAME', $event->getName()); + $this->assertInstanceOf('Composer\Composer', $event->getComposer()); + $this->assertInstanceOf('Composer\IO\IOInterface', $event->getIO()); + $this->assertInstanceOf('Composer\DependencyResolver\PolicyInterface', $event->getPolicy()); + $this->assertInstanceOf('Composer\DependencyResolver\Pool', $event->getPool()); + $this->assertInstanceOf('Composer\Repository\CompositeRepository', $event->getInstalledRepo()); + $this->assertInstanceOf('Composer\DependencyResolver\Request', $event->getRequest()); + $this->assertCount(1, $event->getOperations()); + } +} From be5eae58529c32405ea9d7cbe44d65d84bad0bd7 Mon Sep 17 00:00:00 2001 From: David Neilsen Date: Thu, 31 Jul 2014 10:55:59 +1200 Subject: [PATCH 351/638] Clear all the caches --- src/Composer/Command/ClearCacheCommand.php | 35 ++++++++++++++-------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index f49b00bc3..cf25491d3 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -40,21 +40,30 @@ EOT { $config = Factory::createConfig(); $io = $this->getIO(); - - $cachePath = realpath($config->get('cache-repo-dir')); - if (!$cachePath) { - $io->write('Cache directory does not exist.'); - return; + + $cachePaths = array( + $config->get('cache-dir'), + $config->get('cache-files-dir'), + $config->get('cache-repo-dir'), + $config->get('cache-vcs-dir'), + ); + + foreach ($cachePaths as $cachePath) { + $cachePath = realpath($cachePath); + if (!$cachePath) { + $io->write('Cache directory does not exist.'); + return; + } + $cache = new Cache($io, $cachePath); + if (!$cache->isEnabled()) { + $io->write('Cache is not enabled.'); + return; + } + + $io->write('Clearing cache in: '.$cachePath.''); + $cache->gc(0, 0); } - $cache = new Cache($io, $cachePath); - if (!$cache->isEnabled()) { - $io->write('Cache is not enabled.'); - return; - } - - $io->write('Clearing cache in: '.$cachePath.''); - $cache->gc(0, 0); $io->write('Cache cleared.'); } } From 9d5106e2e32fb18793d9714fc6acf0e7b20d343d Mon Sep 17 00:00:00 2001 From: jakoch Date: Wed, 6 Aug 2014 14:54:43 +0200 Subject: [PATCH 352/638] added stof's php inline example from #3190 to doc/articles/scripts.md --- doc/articles/scripts.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index adbf07f6b..5a2e90014 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -77,6 +77,9 @@ Script definition example: "post-install-cmd": [ "MyVendor\\MyClass::warmCache", "phpunit -c app/" + ], + "post-create-project-cmd" : [ + "php -r \"copy('config/local-example.php', 'config/local.php');\"" ] } } From e6fdefb86996230bc899bb939de9c7c079d3eda6 Mon Sep 17 00:00:00 2001 From: Thai Phan Date: Sun, 10 Aug 2014 23:13:08 +1000 Subject: [PATCH 353/638] Update RuleWatchNode.php --- src/Composer/DependencyResolver/RuleWatchNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/RuleWatchNode.php b/src/Composer/DependencyResolver/RuleWatchNode.php index a08337f5e..59482ffdb 100644 --- a/src/Composer/DependencyResolver/RuleWatchNode.php +++ b/src/Composer/DependencyResolver/RuleWatchNode.php @@ -54,7 +54,7 @@ class RuleWatchNode $literals = $this->rule->getLiterals(); // if there are only 2 elements, both are being watched anyway - if ($literals < 3) { + if (count($literals) < 3) { return; } From 957f498419dec86ab08b80e5e45c855a653f9a5f Mon Sep 17 00:00:00 2001 From: David Neilsen Date: Wed, 13 Aug 2014 09:18:03 +1200 Subject: [PATCH 354/638] Update clear cache command to be more verbose --- src/Composer/Command/ClearCacheCommand.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index cf25491d3..47d139144 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -42,28 +42,28 @@ EOT $io = $this->getIO(); $cachePaths = array( - $config->get('cache-dir'), - $config->get('cache-files-dir'), - $config->get('cache-repo-dir'), - $config->get('cache-vcs-dir'), + 'cache-dir' => $config->get('cache-dir'), + 'cache-files-dir' => $config->get('cache-files-dir'), + 'cache-repo-dir' => $config->get('cache-repo-dir'), + 'cache-vcs-dir' => $config->get('cache-vcs-dir'), ); - foreach ($cachePaths as $cachePath) { + foreach ($cachePaths as $key => $cachePath) { $cachePath = realpath($cachePath); if (!$cachePath) { - $io->write('Cache directory does not exist.'); + $io->write("Cache directory does not exist ($key): $cachePath"); return; } $cache = new Cache($io, $cachePath); if (!$cache->isEnabled()) { - $io->write('Cache is not enabled.'); + $io->write("Cache is not enabled ($key): $cachePath"); return; } - $io->write('Clearing cache in: '.$cachePath.''); + $io->write("Clearing cache ($key): $cachePath"); $cache->gc(0, 0); } - $io->write('Cache cleared.'); + $io->write('All caches cleared.'); } } From 6edf40ee96a754512bde275df7d684337efc0b0f Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 17:48:38 +0000 Subject: [PATCH 355/638] When $TMPDIR is low on space PHP streams silently fail --- src/Composer/Util/RemoteFilesystem.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index d3ecec03d..9f0235b06 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -179,6 +179,10 @@ class RemoteFilesystem // work around issue with gzuncompress & co that do not work with all gzip checksums $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result)); } + + if (!$result) { + throw new TransportException('Failed to decode zlib stream'); + } } } From 439095e446ede68da8f4c6b8c225c5e188e0e252 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 10 Jan 2014 17:49:09 +0000 Subject: [PATCH 356/638] Handle incomplete file write to cache --- src/Composer/Cache.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 6bdf43d5d..83f8f1763 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -83,7 +83,28 @@ class Cache $this->io->write('Writing '.$this->root . $file.' into cache'); } - return file_put_contents($this->root . $file, $contents); + try { + return file_put_contents($this->root . $file, $contents); + } catch(\ErrorException $e) { + if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) { + // Remove partial file. + unlink($this->root . $file); + + $message = sprintf( + 'Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$u bytes of free space available', + $this->root . $file, + $m[1], + $m[2], + @disk_free_space($this->root . dirname($file)) + ); + + $this->io->write($message); + + return false; + } + + throw $e; + } } return false; From fe33d7a34742ce6a16693944996affd99d79ff21 Mon Sep 17 00:00:00 2001 From: Andrew Kreps Date: Fri, 15 Aug 2014 11:28:18 -0700 Subject: [PATCH 357/638] Updated to reflect new tap names for php in OSX homebrew. --- doc/00-intro.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index f86ee400f..81b8c9295 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -112,11 +112,9 @@ Composer is part of the homebrew-php project. ```sh brew update -brew tap homebrew/homebrew-php brew tap homebrew/dupes -brew tap homebrew/versions -brew install php55-intl -brew install homebrew/php/composer +brew tap homebrew/php +brew install composer ``` ## Installation - Windows From 553288a384002631434505a76b732826318ca2b9 Mon Sep 17 00:00:00 2001 From: Richard K Miller Date: Thu, 28 Aug 2014 21:49:57 -0600 Subject: [PATCH 358/638] Very tiny but necessary edit To delete `.git` repos of submodules, `rm -rf vendor/**/.git` doesn't work, but `rm -rf vendor/*/*/.git` does --- .../should-i-commit-the-dependencies-in-my-vendor-directory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md b/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md index 8e50f7264..ed128c88a 100644 --- a/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md +++ b/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md @@ -23,7 +23,7 @@ If you really feel like you must do this, you have a few options: 2. Use --prefer-dist or set `preferred-install` to `dist` in your [config](../04-schema.md#config). 3. Remove the `.git` directory of every dependency after the installation, then - you can add them to your git repo. You can do that with `rm -rf vendor/**/.git` + you can add them to your git repo. You can do that with `rm -rf vendor/*/*/.git` but this means you will have to delete those dependencies from disk before running composer update. 4. Add a .gitignore rule (`vendor/.git`) to ignore all the vendor `.git` folders. From 1ee0f28e79c2327c15b1df42631b895d908afad5 Mon Sep 17 00:00:00 2001 From: Richard K Miller Date: Fri, 29 Aug 2014 13:50:25 -0600 Subject: [PATCH 359/638] update documentation to work for ZSH or Bash --- .../should-i-commit-the-dependencies-in-my-vendor-directory.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md b/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md index ed128c88a..cdbd3141c 100644 --- a/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md +++ b/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md @@ -23,7 +23,8 @@ If you really feel like you must do this, you have a few options: 2. Use --prefer-dist or set `preferred-install` to `dist` in your [config](../04-schema.md#config). 3. Remove the `.git` directory of every dependency after the installation, then - you can add them to your git repo. You can do that with `rm -rf vendor/*/*/.git` + you can add them to your git repo. You can do that with `rm -rf vendor/**/.git` + in ZSH or `find vendor/ -type d -name ".git" -exec rm -rf {} \;` in Bash. but this means you will have to delete those dependencies from disk before running composer update. 4. Add a .gitignore rule (`vendor/.git`) to ignore all the vendor `.git` folders. From d45996111f5b48af4ff55efa2d39972bfca4a443 Mon Sep 17 00:00:00 2001 From: jakoch Date: Wed, 3 Sep 2014 17:05:46 +0200 Subject: [PATCH 360/638] docu fix (removed dangling "times." introduced by 22afc074a96d27b5462d335e22da893570bf7e87) --- doc/03-cli.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index ea0708ae5..7145132c8 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -276,7 +276,6 @@ in your browser. ### Options * **--homepage (-H):** Open the homepage instead of the repository URL. - times. ## depends From e1a3df3dbfe16ae928ec78318b71941ff9f319cf Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 25 Aug 2014 21:52:36 +0200 Subject: [PATCH 361/638] Fixed the registration of commands with invalid JSON file When the JSON file in the current folder is invalid, the registration of custom commands should be ignored rather than breaking the usage of Composer entirely. Closes #3165 --- src/Composer/Console/Application.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 4e2fa14b2..524b01df3 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -113,8 +113,7 @@ class Application extends BaseApplication // add non-standard scripts as own commands $file = Factory::getComposerFile(); - $json = new JsonFile($file); - if ($json->exists() && is_readable($file) && ($composer = $json->read())) { + if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (isset($composer['scripts']) && is_array($composer['scripts'])) { foreach ($composer['scripts'] as $script => $dummy) { if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { From d18ac81381ecc34cc2b3f51223b73b02199147df Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Sep 2014 16:39:00 +0100 Subject: [PATCH 362/638] Fix unset order to avoid a warning --- src/Composer/Package/Loader/ValidatingArrayLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index aef24f891..3493d3d5b 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -58,8 +58,8 @@ class ValidatingArrayLoader implements LoaderInterface try { $this->versionParser->normalize($this->config['version']); } catch (\Exception $e) { - unset($this->config['version']); $this->errors[] = 'version : invalid value ('.$this->config['version'].'): '.$e->getMessage(); + unset($this->config['version']); } } From ca8ce161e1cebd12e4417bea019c58205ec3c1ed Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Sep 2014 16:46:16 +0100 Subject: [PATCH 363/638] Update jsonlint dep, fixes #3151 --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index e5c6b524c..a20854ecc 100644 --- a/composer.lock +++ b/composer.lock @@ -76,16 +76,16 @@ }, { "name": "seld/jsonlint", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "9cae56dbe34f4392e7d0f559474df33749a39f8d" + "reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9cae56dbe34f4392e7d0f559474df33749a39f8d", - "reference": "9cae56dbe34f4392e7d0f559474df33749a39f8d", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/a7bc2ec9520ad15382292591b617c43bdb1fec35", + "reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35", "shasum": "" }, "require": { @@ -118,7 +118,7 @@ "parser", "validator" ], - "time": "2014-07-20 17:36:11" + "time": "2014-09-05 15:36:20" }, { "name": "symfony/console", From 7dadd22ea4cfd52ea765a76723a21fc3db5b8d80 Mon Sep 17 00:00:00 2001 From: Jonathan Klein Date: Fri, 5 Sep 2014 12:21:50 -0400 Subject: [PATCH 364/638] Fixing a typo in the scripts documentation --- doc/articles/scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index adbf07f6b..652a83db3 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -61,7 +61,7 @@ For any given event: - Scripts execute in the order defined when their corresponding event is fired. - An array of scripts wired to a single event can contain both PHP callbacks -and command-line executables commands. +and command-line executable commands. - PHP classes containing defined callbacks must be autoloadable via Composer's autoload functionality. From a8adbfeb9fc7861deade782938222714168a22a8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Sep 2014 17:28:50 +0100 Subject: [PATCH 365/638] Disallow overriding commands with scripts --- src/Composer/Command/RunScriptCommand.php | 6 +++--- src/Composer/Console/Application.php | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 7f94814a7..f01a5febe 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -89,12 +89,12 @@ EOT putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); } - $args = $input->getArguments(); + $args = $input->getArgument('args'); if (in_array($script, $this->commandEvents)) { - return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); + return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args); } - return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); + return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args); } } diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 524b01df3..266767d9b 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -117,7 +117,11 @@ class Application extends BaseApplication if (isset($composer['scripts']) && is_array($composer['scripts'])) { foreach ($composer['scripts'] as $script => $dummy) { if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { - $this->add(new Command\ScriptAliasCommand($script)); + if ($this->has($script)) { + $output->writeln('A script named '.$script.' would override a native Composer function and has been skipped'); + } else { + $this->add(new Command\ScriptAliasCommand($script)); + } } } } From ec758d95b03dcf94f9e7d020bf62a1ed62700044 Mon Sep 17 00:00:00 2001 From: Kayla Daniels Date: Fri, 5 Sep 2014 12:11:27 -0400 Subject: [PATCH 366/638] updated package variable in gathering requirements --- src/Composer/Command/InitCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index a44546b73..78c3ae211 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -335,9 +335,9 @@ EOT $exactMatch = null; $choices = array(); - foreach ($matches as $position => $package) { - $choices[] = sprintf(' %5s %s', "[$position]", $package['name']); - if ($package['name'] === $package) { + foreach ($matches as $position => $foundPackage) { + $choices[] = sprintf(' %5s %s', "[$position]", $foundPackage['name']); + if ($foundPackage['name'] === $package) { $exactMatch = true; break; } From 0e409a159c4b9a9e9e424bd9cb5fac29123a8b47 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Sep 2014 16:05:11 +0100 Subject: [PATCH 367/638] Add toran to docs besides satis --- .../handling-private-packages-with-satis.md | 20 +++++++++++++------ ...-composer-load-repositories-recursively.md | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 473c4f9ed..53ea6505e 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -2,13 +2,21 @@ tagline: Host your own composer repository --> -# Handling private packages with Satis +# Handling private packages with Satis or Toran Proxy -Satis is a static `composer` repository generator. It is a bit like an ultra- -lightweight, static file-based version of packagist and can be used to host the -metadata of your company's private packages, or your own. It basically acts as -a micro-packagist. You can get it from -[GitHub](http://github.com/composer/satis) or install via CLI: +# Toran Proxy + +[Toran Proxy](https://toranproxy.com/) is a commercial alternative to Satis offering professional support as well as a web UI to manage everything and a better integration with Composer. + +Toran's revenue is also used to pay for Composer and Packagist development and hosting so using it is a good way to support open source financially. You can find more information about how to set it up and use it on the [Toran Proxy](https://toranproxy.com/) website. + +# Satis + +Satis on the other hand is open source but only a a static `composer` +repository generator. It is a bit like an ultra-lightweight, static file-based +version of packagist and can be used to host the metadata of your company's +private packages, or your own. You can get it from [GitHub](http://github.com/composer/satis) +or install via CLI: `php composer.phar create-project composer/satis --stability=dev --keep-vcs`. ## Setup diff --git a/doc/faqs/why-can't-composer-load-repositories-recursively.md b/doc/faqs/why-can't-composer-load-repositories-recursively.md index d81a0f066..0ab44c7d2 100644 --- a/doc/faqs/why-can't-composer-load-repositories-recursively.md +++ b/doc/faqs/why-can't-composer-load-repositories-recursively.md @@ -9,7 +9,7 @@ that the main use of custom VCS & package repositories is to temporarily try some things, or use a fork of a project until your pull request is merged, etc. You should not use them to keep track of private packages. For that you should look into [setting up Satis](../articles/handling-private-packages-with-satis.md) -for your company or even for yourself. +or getting a [Toran Proxy](https://toranproxy.com) license for your company. There are three ways the dependency solver could work with custom repositories: From d79f2b0fd33ee9b89f3d9f1969f43dc3d570a33a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Sep 2014 16:11:05 +0100 Subject: [PATCH 368/638] Adds warning when the version field is set, fixes composer/packagist#438 --- src/Composer/Util/ConfigValidator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 5b33e35ae..71b0ac418 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -94,6 +94,10 @@ class ConfigValidator $warnings[] = 'No license specified, it is recommended to do so. For closed-source software you may use "proprietary" as license.'; } + if (isset($manifest['version'])) { + $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.'; + } + if (!empty($manifest['name']) && preg_match('{[A-Z]}', $manifest['name'])) { $suggestName = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $manifest['name']); $suggestName = strtolower($suggestName); From 9efc4d1e9d05528bb992a2a98847490d761e17a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= Date: Wed, 10 Sep 2014 17:51:02 +0200 Subject: [PATCH 369/638] typo --- doc/articles/handling-private-packages-with-satis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 53ea6505e..0ee0adbca 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -12,7 +12,7 @@ Toran's revenue is also used to pay for Composer and Packagist development and h # Satis -Satis on the other hand is open source but only a a static `composer` +Satis on the other hand is open source but only a static `composer` repository generator. It is a bit like an ultra-lightweight, static file-based version of packagist and can be used to host the metadata of your company's private packages, or your own. You can get it from [GitHub](http://github.com/composer/satis) From b132e4eae067e74c9149f747086a1b0dc45e5e4d Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Thu, 11 Sep 2014 11:48:24 +0200 Subject: [PATCH 370/638] Added cacheCredentials config flag for saved SVN credentials to control the --no-auth-cache flag Example config: { "http-basic": { "svn.example.com": { "username": "user", "password": "password", "cacheCredentials": false } } } --- src/Composer/Util/Svn.php | 4 +++ tests/Composer/Test/Util/SvnTest.php | 42 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 4ec13297f..69aa23ebe 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -295,6 +295,10 @@ class Svn $this->credentials['username'] = $authConfig[$host]['username']; $this->credentials['password'] = $authConfig[$host]['password']; + if (array_key_exists('cacheCredentials', $authConfig[$host])) { + $this->cacheCredentials = (bool) $authConfig[$host]['cacheCredentials']; + } + return $this->hasAuth = true; } diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php index 95bc6691e..d846bcb98 100644 --- a/tests/Composer/Test/Util/SvnTest.php +++ b/tests/Composer/Test/Util/SvnTest.php @@ -72,6 +72,48 @@ class SvnTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->getCmd(" --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); } + public function testCredentialsFromConfigWithCacheCredentialsTrue() { + $url = 'http://svn.apache.org'; + + $config = new Config(); + $config->merge( + array( + 'config' => array( + 'http-basic' => array( + 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar', 'cacheCredentials' => true) + ) + ) + ) + ); + + $svn = new Svn($url, new NullIO, $config); + $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCredentialString'); + $reflMethod->setAccessible(true); + + $this->assertEquals($this->getCmd(" --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); + } + + public function testCredentialsFromConfigWithCacheCredentialsFalse() { + $url = 'http://svn.apache.org'; + + $config = new Config(); + $config->merge( + array( + 'config' => array( + 'http-basic' => array( + 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar', 'cacheCredentials' => false) + ) + ) + ) + ); + + $svn = new Svn($url, new NullIO, $config); + $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCredentialString'); + $reflMethod->setAccessible(true); + + $this->assertEquals($this->getCmd(" --no-auth-cache --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); + } + private function getCmd($cmd) { if (defined('PHP_WINDOWS_VERSION_BUILD')) { From 947db97e337c48f34c6699c11ffc77ccc388e54b Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 5 Jul 2014 13:50:47 -0500 Subject: [PATCH 371/638] [#2492] Removing an unused variable and use statement, fixing phpdoc --- src/Composer/Command/CreateProjectCommand.php | 1 - src/Composer/DependencyResolver/Pool.php | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 632e869df..b37814413 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -242,7 +242,6 @@ EOT } $parser = new VersionParser(); - $candidates = array(); $requirements = $parser->parseNameVersionPairs(array($packageName)); $name = strtolower($requirements[0]['name']); if (!$packageVersion && isset($requirements[0]['version'])) { diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 636a78c0a..d0c660f82 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -15,7 +15,6 @@ namespace Composer\DependencyResolver; use Composer\Package\BasePackage; use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; -use Composer\Package\Link; use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\EmptyConstraint; @@ -25,6 +24,7 @@ use Composer\Repository\ComposerRepository; use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\StreamableRepositoryInterface; use Composer\Repository\PlatformRepository; +use Composer\Package\PackageInterface; /** * A package pool contains repositories that provide packages. @@ -232,7 +232,7 @@ class Pool * packages must match or null to return all * @param bool $mustMatchName Whether the name of returned packages * must match the given name - * @return array A set of packages + * @return PackageInterface[] A set of packages */ public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false) { From 58535a62fa906c73668d27de61494c23238ba84e Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 5 Jul 2014 13:51:15 -0500 Subject: [PATCH 372/638] [#2492] Automatically using the latest version when requiring a package This applies to the init and require commands. Previously: If you ommitted the version of a library, it prompted you to enter a version. New Behavior: If you omit the version, it automatically selects the latest version that is consistent with your minimum-stability flag. Is Jordi mentions, this is consistent with how npm works. --- src/Composer/Command/CreateProjectCommand.php | 18 +++--- src/Composer/Command/InitCommand.php | 63 +++++++++++++++---- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index b37814413..e8201cb2b 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -266,23 +266,14 @@ EOT $pool->addRepository($sourceRepo); $constraint = $packageVersion ? $parser->parseConstraints($packageVersion) : null; - $candidates = $pool->whatProvides($name, $constraint); - foreach ($candidates as $key => $candidate) { - if ($candidate->getName() !== $name) { - unset($candidates[$key]); - } - } + $candidates = $pool->whatProvides($name, $constraint, true); if (!$candidates) { throw new \InvalidArgumentException("Could not find package $name" . ($packageVersion ? " with version $packageVersion." : " with stability $stability.")); } - if (null === $directory) { - $parts = explode("/", $name, 2); - $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); - } - // select highest version if we have many + // logic is repeated in InitCommand $package = reset($candidates); foreach ($candidates as $candidate) { if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { @@ -291,6 +282,11 @@ EOT } unset($candidates); + if (null === $directory) { + $parts = explode("/", $name, 2); + $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); + } + $io->write('Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')'); if ($disablePlugins) { diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index a44546b73..0fa0d7564 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -12,6 +12,7 @@ namespace Composer\Command; +use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Factory; use Composer\Package\BasePackage; @@ -32,6 +33,7 @@ class InitCommand extends Command { private $gitConfig; private $repos; + private $pool; public function parseAuthorString($author) { @@ -284,9 +286,11 @@ EOT protected function findPackages($name) { - $packages = array(); + return $this->getRepos()->search($name); + } - // init repos + protected function getRepos() + { if (!$this->repos) { $this->repos = new CompositeRepository(array_merge( array(new PlatformRepository), @@ -294,7 +298,17 @@ EOT )); } - return $this->repos->search($name); + return $this->repos; + } + + protected function getPool() + { + if (!$this->pool) { + $this->pool = new Pool($this->getComposer()->getPackage()->getMinimumStability()); + $this->pool->addRepository($this->getRepos()); + } + + return $this->pool; } protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array()) @@ -306,15 +320,42 @@ EOT $requires = $this->normalizeRequirements($requires); $result = array(); - foreach ($requires as $key => $requirement) { - if (!isset($requirement['version']) && $input->isInteractive()) { - $question = $dialog->getQuestion('Please provide a version constraint for the '.$requirement['name'].' requirement'); - if ($constraint = $dialog->ask($output, $question)) { - $requirement['version'] = $constraint; - } - } + foreach ($requires as $requirement) { if (!isset($requirement['version'])) { - throw new \InvalidArgumentException('The requirement '.$requirement['name'].' must contain a version constraint'); + + $candidates = $this->getPool()->whatProvides($requirement['name'], null, true); + + if (!$candidates) { + throw new \InvalidArgumentException(sprintf( + 'Could not find any versions for package "%s". Perhaps the name is wrong?', + $requirement['name'] + )); + } + + // select highest version if we have many + // logic is repeated in CreateProjectCommand + $package = reset($candidates); + foreach ($candidates as $candidate) { + if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { + $package = $candidate; + } + } + + if (!$package) { + throw new \Exception(sprintf( + 'No version of the package "%s" could be found that meets your minimum stability requirements of "%s"', + $requirement['name'], + $this->getComposer()->getPackage()->getMinimumStability() + )); + } + + $requirement['version'] = $package->getPrettyVersion(); + + $output->writeln(sprintf( + 'Using version %s for %s', + $requirement['version'], + $requirement['name'] + )); } $result[] = $requirement['name'] . ' ' . $requirement['version']; From 26179cc4b49fc18ac9276b90f6b6a47103e8a36d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 5 Jul 2014 14:09:03 -0500 Subject: [PATCH 373/638] [#2492] Prefixed real versions with ~ when guessing the latest version 2.1.0 > ~2.1.0 v2.1.0 -> ~2.1.0 dev-master -> dev-master --- src/Composer/Command/InitCommand.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 0fa0d7564..f0f41a006 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -349,7 +349,17 @@ EOT )); } - $requirement['version'] = $package->getPrettyVersion(); + $version = $package->getPrettyVersion(); + if (!$package->isDev()) { + // remove the v prefix if there is one + if (substr($version, 0, 1) == 'v') { + $version = substr($version, 1); + } + + // 2.1.0 -> ~2.1.0, 2.0-beta.1 -> ~2.0-beta.1 + $version = '~'.$version; + } + $requirement['version'] = $version; $output->writeln(sprintf( 'Using version %s for %s', From aea2e901a93f6923417d01fe03f6910137ae557c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 14 Jul 2014 21:25:44 -0500 Subject: [PATCH 374/638] Extracting logic into a new class related to selecting the latest version Also refactored InitCommand slightly so that you can use this "latest version" functionality when searching for a package as well. --- src/Composer/Command/CreateProjectCommand.php | 18 +--- src/Composer/Command/InitCommand.php | 93 +++++++++++-------- .../Package/Version/VersionSelector.php | 71 ++++++++++++++ .../Package/Version/VersionSelectorTest.php | 71 ++++++++++++++ 4 files changed, 201 insertions(+), 52 deletions(-) create mode 100644 src/Composer/Package/Version/VersionSelector.php create mode 100644 tests/Composer/Test/Package/Version/VersionSelectorTest.php diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index e8201cb2b..9928692eb 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -21,6 +21,7 @@ use Composer\IO\IOInterface; use Composer\Package\BasePackage; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\Package\Version\VersionSelector; use Composer\Repository\ComposerRepository; use Composer\Repository\CompositeRepository; use Composer\Repository\FilesystemRepository; @@ -265,23 +266,14 @@ EOT $pool = new Pool($stability); $pool->addRepository($sourceRepo); - $constraint = $packageVersion ? $parser->parseConstraints($packageVersion) : null; - $candidates = $pool->whatProvides($name, $constraint, true); + // find the latest version if there are multiple + $versionSelector = new VersionSelector($pool); + $package = $versionSelector->findBestCandidate($name, $packageVersion); - if (!$candidates) { + if (!$package) { throw new \InvalidArgumentException("Could not find package $name" . ($packageVersion ? " with version $packageVersion." : " with stability $stability.")); } - // select highest version if we have many - // logic is repeated in InitCommand - $package = reset($candidates); - foreach ($candidates as $candidate) { - if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { - $package = $candidate; - } - } - unset($candidates); - if (null === $directory) { $parts = explode("/", $name, 2); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index f0f41a006..b0ce567a8 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -16,6 +16,7 @@ use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Factory; use Composer\Package\BasePackage; +use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Package\Version\VersionParser; @@ -323,42 +324,8 @@ EOT foreach ($requires as $requirement) { if (!isset($requirement['version'])) { - $candidates = $this->getPool()->whatProvides($requirement['name'], null, true); - - if (!$candidates) { - throw new \InvalidArgumentException(sprintf( - 'Could not find any versions for package "%s". Perhaps the name is wrong?', - $requirement['name'] - )); - } - - // select highest version if we have many - // logic is repeated in CreateProjectCommand - $package = reset($candidates); - foreach ($candidates as $candidate) { - if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { - $package = $candidate; - } - } - - if (!$package) { - throw new \Exception(sprintf( - 'No version of the package "%s" could be found that meets your minimum stability requirements of "%s"', - $requirement['name'], - $this->getComposer()->getPackage()->getMinimumStability() - )); - } - - $version = $package->getPrettyVersion(); - if (!$package->isDev()) { - // remove the v prefix if there is one - if (substr($version, 0, 1) == 'v') { - $version = substr($version, 1); - } - - // 2.1.0 -> ~2.1.0, 2.0-beta.1 -> ~2.0-beta.1 - $version = '~'.$version; - } + // determine the best version automatically + $version = $this->findBestVersionForPackage($requirement['name']); $requirement['version'] = $version; $output->writeln(sprintf( @@ -420,7 +387,7 @@ EOT $package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add, or the complete package name if it is not listed', false, ':'), $validator, 3); } - // no constraint yet, prompt user + // no constraint yet, determine the best version automatically if (false !== $package && false === strpos($package, ' ')) { $validator = function ($input) { $input = trim($input); @@ -428,9 +395,20 @@ EOT return $input ?: false; }; - $constraint = $dialog->askAndValidate($output, $dialog->getQuestion('Enter the version constraint to require', false, ':'), $validator, 3); + $constraint = $dialog->askAndValidate( + $output, + $dialog->getQuestion('Enter the version constraint to require (or leave blank to use the latest version)', false, ':'), + $validator, + 3) + ; if (false === $constraint) { - continue; + $constraint = $this->findBestVersionForPackage($package); + + $output->writeln(sprintf( + 'Using version %s for %s', + $constraint, + $package + )); } $package .= ' '.$constraint; @@ -555,4 +533,41 @@ EOT return false !== filter_var($email, FILTER_VALIDATE_EMAIL); } + + /** + * Given a package name, this determines the best version to use in the require key. + * + * This returns a version with the ~ operator prefixed when possible. + * + * @param string $name + * @return string + * @throws \InvalidArgumentException + */ + protected function findBestVersionForPackage($name) + { + // find the latest version allowed in this pool + $versionSelector = new VersionSelector($this->getPool()); + $package = $versionSelector->findBestCandidate($name); + + if (!$package) { + throw new \InvalidArgumentException(sprintf( + 'Could not find package %s at any version for your minimum-stability (%s). Check the package spelling or your minimum-stability', + $name, + $this->getComposer()->getPackage()->getMinimumStability() + )); + } + + $version = $package->getPrettyVersion(); + if (!$package->isDev()) { + // remove the v prefix if there is one + if (substr($version, 0, 1) == 'v') { + $version = substr($version, 1); + } + + // 2.1.0 -> ~2.1.0, 2.0-beta.1 -> ~2.0-beta.1 + $version = '~'.$version; + } + + return $version; + } } diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php new file mode 100644 index 000000000..ad9c2ab71 --- /dev/null +++ b/src/Composer/Package/Version/VersionSelector.php @@ -0,0 +1,71 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Version; + +use Composer\DependencyResolver\Pool; +use Composer\Package\PackageInterface; + +/** + * Selects the best possible version for a package + * + * @author Ryan Weaver + */ +class VersionSelector +{ + private $pool; + + private $parser; + + public function __construct(Pool $pool) + { + $this->pool = $pool; + } + + /** + * Given a package name and optional version, returns the latest PackageInterface + * that matches. + * + * @param string $packageName + * @param string $targetPackageVersion + * @return PackageInterface|bool + */ + public function findBestCandidate($packageName, $targetPackageVersion = null) + { + $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; + $candidates = $this->pool->whatProvides($packageName, $constraint, true); + + if (!$candidates) { + return false; + } + + // select highest version if we have many + // logic is repeated in InitCommand + $package = reset($candidates); + foreach ($candidates as $candidate) { + if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { + $package = $candidate; + } + } + + return $package; + } + + private function getParser() + { + if ($this->parser === null) { + $this->parser = new VersionParser(); + } + + return $this->parser; + } +} diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php new file mode 100644 index 000000000..636645807 --- /dev/null +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -0,0 +1,71 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Package\Version; + +use Composer\Package\Version\VersionSelector; + +class VersionSelectorTest extends \PHPUnit_Framework_TestCase +{ + // A) multiple versions, get the latest one + // B) targetPackageVersion will pass to pool + // C) No results, throw exception + + public function testLatestVersionIsReturned() + { + $packageName = 'foobar'; + + $package1 = $this->createMockPackage('1.2.1'); + $package2 = $this->createMockPackage('1.2.2'); + $package3 = $this->createMockPackage('1.2.0'); + $packages = array($package1, $package2, $package3); + + $pool = $this->createMockPool(); + $pool->expects($this->once()) + ->method('whatProvides') + ->with($packageName, null, true) + ->will($this->returnValue($packages)); + + $versionSelector = new VersionSelector($pool); + $best = $versionSelector->findBestCandidate($packageName); + + // 1.2.2 should be returned because it's the latest of the returned versions + $this->assertEquals($package2, $best, 'Latest version should be 1.2.2'); + } + + public function testFalseReturnedOnNoPackages() + { + $pool = $this->createMockPool(); + $pool->expects($this->once()) + ->method('whatProvides') + ->will($this->returnValue(array())); + + $versionSelector = new VersionSelector($pool); + $best = $versionSelector->findBestCandidate('foobaz'); + $this->assertFalse($best, 'No versions are available returns false'); + } + + private function createMockPackage($version) + { + $package = $this->getMock('\Composer\Package\PackageInterface'); + $package->expects($this->any()) + ->method('getVersion') + ->will($this->returnValue($version)); + + return $package; + } + + private function createMockPool() + { + return $this->getMock('Composer\DependencyResolver\Pool', array(), array(), '', true); + } +} From 895e62e85991dfb6f20d7cdc9aed2430c44fd742 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 12 Sep 2014 11:23:20 -0400 Subject: [PATCH 375/638] Refactoring selection of the "recommended" version (e.g ~1.2) and adding some tests This also modifies the behavior slightly (from a recommendation by seldaek) to always propose the minor version of the recommendation (e.g. ~1.2 instead of ~1.2.1). --- src/Composer/Command/InitCommand.php | 13 +----- .../Package/Version/VersionSelector.php | 42 ++++++++++++++++++ .../Package/Version/VersionSelectorTest.php | 44 +++++++++++++++++++ 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index b0ce567a8..37c4d3029 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -557,17 +557,6 @@ EOT )); } - $version = $package->getPrettyVersion(); - if (!$package->isDev()) { - // remove the v prefix if there is one - if (substr($version, 0, 1) == 'v') { - $version = substr($version, 1); - } - - // 2.1.0 -> ~2.1.0, 2.0-beta.1 -> ~2.0-beta.1 - $version = '~'.$version; - } - - return $version; + return $versionSelector->findRecommendedRequireVersion($package); } } diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index ad9c2ab71..c00c4254d 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -60,6 +60,48 @@ class VersionSelector return $package; } + /** + * Given a concrete version, this returns a ~ constraint (when possible) + * that should be used, for example, in composer.json. + * + * For example: + * * 1.2.1 -> ~1.2 + * * 1.2 -> ~1.2 + * * v3.2.1 -> ~3.2 + * * 2.0-beta.1 -> ~2.0-beta.1 + * * dev-master -> dev-master (dev versions are untouched) + * + * @param PackageInterface $package + * @return string + */ + public function findRecommendedRequireVersion(PackageInterface $package) + { + $version = $package->getPrettyVersion(); + if (!$package->isDev()) { + // remove the v prefix if there is one + if (substr($version, 0, 1) == 'v') { + $version = substr($version, 1); + } + + // for stable packages only, we try to transform 2.1.1 to 2.1 + // this allows you to upgrade through minor versions + if ($package->getStability() == 'stable') { + $semanticVersionParts = explode('.', $version); + // check to see if we have a normal 1.2.6 semantic version + if (count($semanticVersionParts) == 3) { + // remove the last part (i.e. the patch version number) + unset($semanticVersionParts[2]); + $version = implode('.', $semanticVersionParts); + } + } + + // 2.1 -> ~2.1 + $version = '~'.$version; + } + + return $version; + } + private function getParser() { if ($this->parser === null) { diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 636645807..fc4d6d61b 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -54,6 +54,50 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase $this->assertFalse($best, 'No versions are available returns false'); } + /** + * @dataProvider getRecommendedRequireVersionPackages + */ + public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion) + { + $pool = $this->createMockPool(); + $versionSelector = new VersionSelector($pool); + + $package = $this->getMock('\Composer\Package\PackageInterface'); + $package->expects($this->any()) + ->method('getPrettyVersion') + ->will($this->returnValue($prettyVersion)); + $package->expects($this->any()) + ->method('isDev') + ->will($this->returnValue($isDev)); + $package->expects($this->any()) + ->method('getStability') + ->will($this->returnValue($stability)); + + $recommended = $versionSelector->findRecommendedRequireVersion($package); + + // assert that the recommended version is what we expect + $this->assertEquals($expectedVersion, $recommended); + } + + public function getRecommendedRequireVersionPackages() + { + return array( + // real version, is dev package, stability, expected recommendation + array('1.2.1', false, 'stable', '~1.2'), + array('1.2', false, 'stable', '~1.2'), + array('v1.2.1', false, 'stable', '~1.2'), + array('3.1.2-pl2', false, 'stable', '~3.1'), + array('3.1.2-patch', false, 'stable', '~3.1'), + // for non-stable versions, we add ~, but don't try the (1.2.1 -> 1.2) transformation + array('2.0-beta.1', false, 'beta', '~2.0-beta.1'), + array('3.1.2-alpha5', false, 'alpha', '~3.1.2-alpha5'), + array('3.0-RC2', false, 'RC', '~3.0-RC2'), + // dev packages are not touched at all + array('dev-master', true, 'dev', 'dev-master'), + array('3.1.2-dev', true, 'dev', '3.1.2-dev'), + ); + } + private function createMockPackage($version) { $package = $this->getMock('\Composer\Package\PackageInterface'); From 0d0ed59e5ce07857557924efd14d8ce2d996d3f4 Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Mon, 15 Sep 2014 13:21:33 +0200 Subject: [PATCH 376/638] Codestyle fix --- tests/Composer/Test/Util/SvnTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php index d846bcb98..9ee5d1271 100644 --- a/tests/Composer/Test/Util/SvnTest.php +++ b/tests/Composer/Test/Util/SvnTest.php @@ -72,7 +72,8 @@ class SvnTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->getCmd(" --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); } - public function testCredentialsFromConfigWithCacheCredentialsTrue() { + public function testCredentialsFromConfigWithCacheCredentialsTrue() + { $url = 'http://svn.apache.org'; $config = new Config(); @@ -93,7 +94,8 @@ class SvnTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->getCmd(" --username 'foo' --password 'bar' "), $reflMethod->invoke($svn)); } - public function testCredentialsFromConfigWithCacheCredentialsFalse() { + public function testCredentialsFromConfigWithCacheCredentialsFalse() + { $url = 'http://svn.apache.org'; $config = new Config(); From 204fc207faa0fddf9e69a6e958242a266d02540b Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Tue, 16 Sep 2014 15:16:55 +0200 Subject: [PATCH 377/638] Moved config option to not save subversion credentials to repository configuration, added documentation. --- doc/05-repositories.md | 33 ++++++++++++++++++++++- src/Composer/Repository/Vcs/SvnDriver.php | 9 +++++++ src/Composer/Util/Svn.php | 12 ++++++--- tests/Composer/Test/Util/SvnTest.php | 6 +++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 049a05170..1e6128f0e 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -345,6 +345,37 @@ If the package is in a sub-directory, e.g. `/trunk/foo/bar/composer.json` and setting the `"package-path"` option to the sub-directory, in this example it would be `"package-path": "foo/bar/"`. +If you have a private Subversion repository you can safe credentials in the +http-basic section of your config (See [Schema](04-schema.md)): + +```json +{ + "http-basic": { + "svn.example.org": { + "username": "username", + "password": "password" + } + } +} +``` + +If your Subversion client is configured to store credentials by default these +credentials will be saved for the current user and existing saved credentials +for this server will be overwritten. To change this behavior by setting the +`"cache-credentials"` option in your repository configuration: + +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "http://svn.example.org/projectA/", + "cache-credentials": false + } + ] +} +``` + ### PEAR It is possible to install packages from any PEAR channel by using the `pear` @@ -494,7 +525,7 @@ there are some use cases for hosting your own repository. might want to keep them separate to packagist. An example of this would be wordpress plugins. -For hosting your own packages, a native `composer` type of repository is +For hosting your own packages, a native `composer` type of repository is recommended, which provides the best performance. There are a few tools that can help you create a `composer` repository. diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2bddbe15d..f2a9e1de0 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -27,6 +27,10 @@ use Composer\Downloader\TransportException; */ class SvnDriver extends VcsDriver { + + /** + * @var Cache + */ protected $cache; protected $baseUrl; protected $tags; @@ -38,6 +42,7 @@ class SvnDriver extends VcsDriver protected $branchesPath = 'branches'; protected $tagsPath = 'tags'; protected $packagePath = ''; + protected $cacheCredentials = true; /** * @var \Composer\Util\Svn @@ -62,6 +67,9 @@ class SvnDriver extends VcsDriver if (isset($this->repoConfig['tags-path'])) { $this->tagsPath = $this->repoConfig['tags-path']; } + if (array_key_exists('cache-credentials', $this->repoConfig)) { + $this->cacheCredentials = (bool) $this->repoConfig['cache-credentials']; + } if (isset($this->repoConfig['package-path'])) { $this->packagePath = '/' . trim($this->repoConfig['package-path'], '/'); } @@ -307,6 +315,7 @@ class SvnDriver extends VcsDriver { if (null === $this->util) { $this->util = new SvnUtil($this->baseUrl, $this->io, $this->config, $this->process); + $this->util->setCacheCredentials($this->cacheCredentials); } try { diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 69aa23ebe..6d659a7f1 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -147,6 +147,14 @@ class Svn ); } + /** + * @param boolean $cacheCredentials + */ + public function setCacheCredentials($cacheCredentials) + { + $this->cacheCredentials = $cacheCredentials; + } + /** * Repositories requests credentials, let's put them in. * @@ -295,10 +303,6 @@ class Svn $this->credentials['username'] = $authConfig[$host]['username']; $this->credentials['password'] = $authConfig[$host]['password']; - if (array_key_exists('cacheCredentials', $authConfig[$host])) { - $this->cacheCredentials = (bool) $authConfig[$host]['cacheCredentials']; - } - return $this->hasAuth = true; } diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php index 9ee5d1271..b1f19ca1a 100644 --- a/tests/Composer/Test/Util/SvnTest.php +++ b/tests/Composer/Test/Util/SvnTest.php @@ -81,13 +81,14 @@ class SvnTest extends \PHPUnit_Framework_TestCase array( 'config' => array( 'http-basic' => array( - 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar', 'cacheCredentials' => true) + 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar') ) ) ) ); $svn = new Svn($url, new NullIO, $config); + $svn->setCacheCredentials(true); $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCredentialString'); $reflMethod->setAccessible(true); @@ -103,13 +104,14 @@ class SvnTest extends \PHPUnit_Framework_TestCase array( 'config' => array( 'http-basic' => array( - 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar', 'cacheCredentials' => false) + 'svn.apache.org' => array('username' => 'foo', 'password' => 'bar') ) ) ) ); $svn = new Svn($url, new NullIO, $config); + $svn->setCacheCredentials(false); $reflMethod = new \ReflectionMethod('Composer\\Util\\Svn', 'getCredentialString'); $reflMethod->setAccessible(true); From a76436bbae11c05d3cc25990d4cc96eb4afd58c9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 20 Sep 2014 12:42:36 +1000 Subject: [PATCH 378/638] Update handling-private-packages-with-satis.md --- doc/articles/handling-private-packages-with-satis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 53ea6505e..0ee0adbca 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -12,7 +12,7 @@ Toran's revenue is also used to pay for Composer and Packagist development and h # Satis -Satis on the other hand is open source but only a a static `composer` +Satis on the other hand is open source but only a static `composer` repository generator. It is a bit like an ultra-lightweight, static file-based version of packagist and can be used to host the metadata of your company's private packages, or your own. You can get it from [GitHub](http://github.com/composer/satis) From 6c6a623229431e90c12b5fb0b6991e0a0b58ef76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Mon, 22 Sep 2014 11:58:26 +0200 Subject: [PATCH 379/638] Fix invalid version format --- .../Package/Version/VersionParser.php | 6 ++ .../Package/Version/VersionParserTest.php | 57 ++++++++++--------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 812ccf5ad..3825426a8 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -122,6 +122,12 @@ class VersionParser } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)'.self::$modifierRegex.'$}i', $version, $matches)) { // match date-based versioning $version = preg_replace('{\D}', '-', $matches[1]); $index = 2; + } elseif (preg_match('{^v?(\d{4,})(\.\d+)?(\.\d+)?(\.\d+)?'.self::$modifierRegex.'$}i', $version, $matches)) { + $version = $matches[1] + .(!empty($matches[2]) ? $matches[2] : '.0') + .(!empty($matches[3]) ? $matches[3] : '.0') + .(!empty($matches[4]) ? $matches[4] : '.0'); + $index = 5; } // add version modifiers if a version was matched diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 464779d83..f54371d65 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -79,34 +79,35 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase public function successfulNormalizedVersions() { return array( - 'none' => array('1.0.0', '1.0.0.0'), - 'none/2' => array('1.2.3.4', '1.2.3.4'), - 'parses state' => array('1.0.0RC1dev', '1.0.0.0-RC1-dev'), - 'CI parsing' => array('1.0.0-rC15-dev', '1.0.0.0-RC15-dev'), - 'delimiters' => array('1.0.0.RC.15-dev', '1.0.0.0-RC15-dev'), - 'RC uppercase' => array('1.0.0-rc1', '1.0.0.0-RC1'), - 'patch replace' => array('1.0.0.pl3-dev', '1.0.0.0-patch3-dev'), - 'forces w.x.y.z' => array('1.0-dev', '1.0.0.0-dev'), - 'forces w.x.y.z/2' => array('0', '0.0.0.0'), - 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), - 'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'), - 'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'), - 'strips leading v' => array('v1.0.0', '1.0.0.0'), - 'strips v/datetime' => array('v20100102', '20100102'), - 'parses dates y-m' => array('2010.01', '2010-01'), - 'parses dates w/ .' => array('2010.01.02', '2010-01-02'), - 'parses dates w/ -' => array('2010-01-02', '2010-01-02'), - 'parses numbers' => array('2010-01-02.5', '2010-01-02-5'), - 'parses datetime' => array('20100102-203040', '20100102-203040'), - 'parses dt+number' => array('20100102203040-10', '20100102203040-10'), - 'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'), - 'parses master' => array('dev-master', '9999999-dev'), - 'parses trunk' => array('dev-trunk', '9999999-dev'), - 'parses branches' => array('1.x-dev', '1.9999999.9999999.9999999-dev'), - 'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'), - 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-FOOBAR'), - 'parses arbitrary3' => array('dev-feature/foo', 'dev-feature/foo'), - 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), + 'none' => array('1.0.0', '1.0.0.0'), + 'none/2' => array('1.2.3.4', '1.2.3.4'), + 'parses state' => array('1.0.0RC1dev', '1.0.0.0-RC1-dev'), + 'CI parsing' => array('1.0.0-rC15-dev', '1.0.0.0-RC15-dev'), + 'delimiters' => array('1.0.0.RC.15-dev', '1.0.0.0-RC15-dev'), + 'RC uppercase' => array('1.0.0-rc1', '1.0.0.0-RC1'), + 'patch replace' => array('1.0.0.pl3-dev', '1.0.0.0-patch3-dev'), + 'forces w.x.y.z' => array('1.0-dev', '1.0.0.0-dev'), + 'forces w.x.y.z/2' => array('0', '0.0.0.0'), + 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), + 'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'), + 'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'), + 'strips leading v' => array('v1.0.0', '1.0.0.0'), + 'strips v/datetime' => array('v20100102', '20100102'), + 'parses dates y-m' => array('2010.01', '2010-01'), + 'parses dates w/ .' => array('2010.01.02', '2010-01-02'), + 'parses dates w/ -' => array('2010-01-02', '2010-01-02'), + 'parses numbers' => array('2010-01-02.5', '2010-01-02-5'), + 'parses dates y.m.Y' => array('2010.1.555', '2010.1.555.0'), + 'parses datetime' => array('20100102-203040', '20100102-203040'), + 'parses dt+number' => array('20100102203040-10', '20100102203040-10'), + 'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'), + 'parses master' => array('dev-master', '9999999-dev'), + 'parses trunk' => array('dev-trunk', '9999999-dev'), + 'parses branches' => array('1.x-dev', '1.9999999.9999999.9999999-dev'), + 'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'), + 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-FOOBAR'), + 'parses arbitrary3' => array('dev-feature/foo', 'dev-feature/foo'), + 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), ); } From 2c237fdfdfa18cdc69813783a1cc9c219efd1d78 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 22 Sep 2014 15:09:55 +0100 Subject: [PATCH 380/638] Handle files in Filesystem::copyThenRemove, fixes #3287 --- src/Composer/Util/Filesystem.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 182004205..6044c25d6 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -234,6 +234,13 @@ class Filesystem */ public function copyThenRemove($source, $target) { + if (!is_dir($source)) { + copy($source, $target); + $this->unlink($source); + + return; + } + $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS); $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST); $this->ensureDirectoryExists($target); From 4d522e40fbd770aff93aec75145cf77a5ef27293 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 22 Sep 2014 16:05:43 +0100 Subject: [PATCH 381/638] Load auth when updating a git repo mirror, fixes #3243 --- src/Composer/Repository/Vcs/GitDriver.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index f37be1e9a..20cfa93fc 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -56,16 +56,22 @@ class GitDriver extends VcsDriver throw new \InvalidArgumentException('The source URL '.$this->url.' is invalid, ssh URLs should have a port number after ":".'."\n".'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); } + $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs); + // update the repo if it is a valid git repository if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') { - if (0 !== $this->process->execute('git remote update --prune origin', $output, $this->repoDir)) { - $this->io->write('Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')'); + try { + $commandCallable = function ($url) { + return sprintf('git remote set-url origin %s && git remote update --prune origin', escapeshellarg($url)); + }; + $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir); + } catch (\Exception $e) { + $this->io->write('Failed to update '.$this->url.', package information from this repository may be outdated ('.$e->getMessage().')'); } } else { // clean up directory and do a fresh clone into it $fs->removeDirectory($this->repoDir); - $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs); $repoDir = $this->repoDir; $commandCallable = function ($url) use ($repoDir) { return sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($repoDir)); From 62b506214640ae66ad88a07240a0bbb0201bd1e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 22 Sep 2014 17:04:58 +0100 Subject: [PATCH 382/638] Improve sorting of plugin packages, fixes #3109, refs #2972 --- src/Composer/Installer.php | 16 +++++++++++++--- .../installer/plugins-are-installed-first.test | 11 +++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 3176ece17..5191d8558 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -597,9 +597,19 @@ class Installer continue; } - if ($package->getRequires() === array() && ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer')) { - $installerOps[] = $op; - unset($operations[$idx]); + if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') { + // ignore requirements to platform or composer-plugin-api + $requires = array_keys($package->getRequires()); + foreach ($requires as $index => $req) { + if ($req === 'composer-plugin-api' || preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { + unset($requires[$index]); + } + } + // if there are no other requirements, move the plugin to the top of the op list + if (!count($requires)) { + $installerOps[] = $op; + unset($operations[$idx]); + } } } diff --git a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test index c57a36d35..ad34e9c02 100644 --- a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test +++ b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test @@ -1,5 +1,5 @@ --TEST-- -Composer installers are installed first if they have no requirements +Composer installers are installed first if they have no meaningful requirements --COMPOSER-- { "repositories": [ @@ -9,20 +9,23 @@ Composer installers are installed first if they have no requirements { "name": "pkg", "version": "1.0.0" }, { "name": "pkg2", "version": "1.0.0" }, { "name": "inst", "version": "1.0.0", "type": "composer-plugin" }, - { "name": "inst2", "version": "1.0.0", "type": "composer-plugin", "require": { "pkg2": "*" } } + { "name": "inst-with-req", "version": "1.0.0", "type": "composer-plugin", "require": { "php": ">=5", "ext-json": "*", "composer-plugin-api": "*" } }, + { "name": "inst-with-req2", "version": "1.0.0", "type": "composer-plugin", "require": { "pkg2": "*" } } ] } ], "require": { "pkg": "1.0.0", "inst": "1.0.0", - "inst2": "1.0.0" + "inst-with-req2": "1.0.0", + "inst-with-req": "1.0.0" } } --RUN-- install --EXPECT-- Installing inst (1.0.0) +Installing inst-with-req (1.0.0) Installing pkg (1.0.0) Installing pkg2 (1.0.0) -Installing inst2 (1.0.0) +Installing inst-with-req2 (1.0.0) From b7b0901f87a3dc293027a343ae868dc3c24458fa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 23 Sep 2014 15:17:53 +0100 Subject: [PATCH 383/638] Allow using new code in the init command and avoid initializing the composer instance too early, refs #3096 --- src/Composer/Command/InitCommand.php | 49 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 37c4d3029..d7ee60677 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -302,16 +302,6 @@ EOT return $this->repos; } - protected function getPool() - { - if (!$this->pool) { - $this->pool = new Pool($this->getComposer()->getPackage()->getMinimumStability()); - $this->pool->addRepository($this->getRepos()); - } - - return $this->pool; - } - protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array()) { $dialog = $this->getHelperSet()->get('dialog'); @@ -325,7 +315,7 @@ EOT if (!isset($requirement['version'])) { // determine the best version automatically - $version = $this->findBestVersionForPackage($requirement['name']); + $version = $this->findBestVersionForPackage($input, $requirement['name']); $requirement['version'] = $version; $output->writeln(sprintf( @@ -402,7 +392,7 @@ EOT 3) ; if (false === $constraint) { - $constraint = $this->findBestVersionForPackage($package); + $constraint = $this->findBestVersionForPackage($input, $package); $output->writeln(sprintf( 'Using version %s for %s', @@ -534,26 +524,53 @@ EOT return false !== filter_var($email, FILTER_VALIDATE_EMAIL); } + private function getPool(InputInterface $input) + { + if (!$this->pool) { + $this->pool = new Pool($this->getMinimumStability($input)); + $this->pool->addRepository($this->getRepos()); + } + + return $this->pool; + } + + private function getMinimumStability(InputInterface $input) + { + if ($input->hasOption('stability')) { + return $input->getOption('stability') ?: 'stable'; + } + + $file = Factory::getComposerFile(); + if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { + if (!empty($composer['minimum-stability'])) { + return $composer['minimum-stability']; + } + } + + return 'stable'; + } + /** * Given a package name, this determines the best version to use in the require key. * * This returns a version with the ~ operator prefixed when possible. * - * @param string $name + * @param InputInterface $input + * @param string $name * @return string * @throws \InvalidArgumentException */ - protected function findBestVersionForPackage($name) + private function findBestVersionForPackage(InputInterface $input, $name) { // find the latest version allowed in this pool - $versionSelector = new VersionSelector($this->getPool()); + $versionSelector = new VersionSelector($this->getPool($input)); $package = $versionSelector->findBestCandidate($name); if (!$package) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s at any version for your minimum-stability (%s). Check the package spelling or your minimum-stability', $name, - $this->getComposer()->getPackage()->getMinimumStability() + $this->getMinimumStability($input) )); } From e6165e649524f2d8edec6101fd740dd4dfeef7cc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 23 Sep 2014 15:19:23 +0100 Subject: [PATCH 384/638] Tweak rules to allow guessing based on branch aliases and avoid greedy constraints on alpha/beta/RC packages, refs #3096 --- .../Package/Version/VersionSelector.php | 64 ++++++++++++------- .../Package/Version/VersionSelectorTest.php | 29 +++++++-- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index c00c4254d..29358027f 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -14,6 +14,8 @@ namespace Composer\Package\Version; use Composer\DependencyResolver\Pool; use Composer\Package\PackageInterface; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Dumper\ArrayDumper; /** * Selects the best possible version for a package @@ -49,7 +51,6 @@ class VersionSelector } // select highest version if we have many - // logic is repeated in InitCommand $package = reset($candidates); foreach ($candidates as $candidate) { if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { @@ -68,7 +69,8 @@ class VersionSelector * * 1.2.1 -> ~1.2 * * 1.2 -> ~1.2 * * v3.2.1 -> ~3.2 - * * 2.0-beta.1 -> ~2.0-beta.1 + * * 2.0-beta.1 -> ~2.0@beta + * * dev-master -> ~2.1@dev (dev version with alias) * * dev-master -> dev-master (dev versions are untouched) * * @param PackageInterface $package @@ -76,30 +78,46 @@ class VersionSelector */ public function findRecommendedRequireVersion(PackageInterface $package) { - $version = $package->getPrettyVersion(); + $version = $package->getVersion(); if (!$package->isDev()) { - // remove the v prefix if there is one - if (substr($version, 0, 1) == 'v') { - $version = substr($version, 1); - } - - // for stable packages only, we try to transform 2.1.1 to 2.1 - // this allows you to upgrade through minor versions - if ($package->getStability() == 'stable') { - $semanticVersionParts = explode('.', $version); - // check to see if we have a normal 1.2.6 semantic version - if (count($semanticVersionParts) == 3) { - // remove the last part (i.e. the patch version number) - unset($semanticVersionParts[2]); - $version = implode('.', $semanticVersionParts); - } - } - - // 2.1 -> ~2.1 - $version = '~'.$version; + return $this->transformVersion($version, $package->getPrettyVersion(), $package->getStability()); } - return $version; + $loader = new ArrayLoader($this->getParser()); + $dumper = new ArrayDumper(); + $extra = $loader->getBranchAlias($dumper->dump($package)); + if ($extra) { + $extra = preg_replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count); + if ($count) { + $extra = str_replace('.9999999', '.0', $extra); + return $this->transformVersion($extra, $extra, 'dev'); + } + } + + return $package->getPrettyVersion(); + } + + private function transformVersion($version, $prettyVersion, $stability) + { + // attempt to transform 2.1.1 to 2.1 + // this allows you to upgrade through minor versions + $semanticVersionParts = explode('.', $version); + // check to see if we have a semver-looking version + if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) { + // remove the last parts (i.e. the patch version number and any extra) + unset($semanticVersionParts[2], $semanticVersionParts[3]); + $version = implode('.', $semanticVersionParts); + } else { + return $prettyVersion; + } + + // append stability flag if not default + if ($stability != 'stable') { + $version .= '@'.$stability; + } + + // 2.1 -> ~2.1 + return '~'.$version; } private function getParser() diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index fc4d6d61b..2a9cb45a0 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -13,6 +13,7 @@ namespace Composer\Test\Package\Version; use Composer\Package\Version\VersionSelector; +use Composer\Package\Version\VersionParser; class VersionSelectorTest extends \PHPUnit_Framework_TestCase { @@ -57,15 +58,19 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase /** * @dataProvider getRecommendedRequireVersionPackages */ - public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion) + public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion, $branchAlias = null) { $pool = $this->createMockPool(); $versionSelector = new VersionSelector($pool); + $versionParser = new VersionParser(); $package = $this->getMock('\Composer\Package\PackageInterface'); $package->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue($prettyVersion)); + $package->expects($this->any()) + ->method('getVersion') + ->will($this->returnValue($versionParser->normalize($prettyVersion))); $package->expects($this->any()) ->method('isDev') ->will($this->returnValue($isDev)); @@ -73,6 +78,11 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase ->method('getStability') ->will($this->returnValue($stability)); + $branchAlias = $branchAlias === null ? array() : array('branch-alias' => array($prettyVersion => $branchAlias)); + $package->expects($this->any()) + ->method('getExtra') + ->will($this->returnValue($branchAlias)); + $recommended = $versionSelector->findRecommendedRequireVersion($package); // assert that the recommended version is what we expect @@ -82,19 +92,26 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase public function getRecommendedRequireVersionPackages() { return array( - // real version, is dev package, stability, expected recommendation + // real version, is dev package, stability, expected recommendation, [branch-alias] array('1.2.1', false, 'stable', '~1.2'), array('1.2', false, 'stable', '~1.2'), array('v1.2.1', false, 'stable', '~1.2'), array('3.1.2-pl2', false, 'stable', '~3.1'), array('3.1.2-patch', false, 'stable', '~3.1'), // for non-stable versions, we add ~, but don't try the (1.2.1 -> 1.2) transformation - array('2.0-beta.1', false, 'beta', '~2.0-beta.1'), - array('3.1.2-alpha5', false, 'alpha', '~3.1.2-alpha5'), - array('3.0-RC2', false, 'RC', '~3.0-RC2'), - // dev packages are not touched at all + array('2.0-beta.1', false, 'beta', '~2.0@beta'), + array('3.1.2-alpha5', false, 'alpha', '~3.1@alpha'), + array('3.0-RC2', false, 'RC', '~3.0@RC'), + // date-based versions are not touched at all + array('v20121020', false, 'stable', 'v20121020'), + array('v20121020.2', false, 'stable', 'v20121020.2'), + // dev packages without alias are not touched at all array('dev-master', true, 'dev', 'dev-master'), array('3.1.2-dev', true, 'dev', '3.1.2-dev'), + // dev packages with alias inherit the alias + array('dev-master', true, 'dev', '~2.1@dev', '2.1.x-dev'), + array('dev-master', true, 'dev', '~2.1@dev', '2.1.3.x-dev'), + array('dev-master', true, 'dev', '~2.0@dev', '2.x-dev'), ); } From f8023cffdf7f23a12eeefef73ff7a77f476c0bdc Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Wed, 24 Sep 2014 13:24:54 +0200 Subject: [PATCH 385/638] renamed cache-credentials to svn-cache-credentials --- doc/05-repositories.md | 4 ++-- src/Composer/Repository/Vcs/SvnDriver.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 1e6128f0e..318ea65bb 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -362,7 +362,7 @@ http-basic section of your config (See [Schema](04-schema.md)): If your Subversion client is configured to store credentials by default these credentials will be saved for the current user and existing saved credentials for this server will be overwritten. To change this behavior by setting the -`"cache-credentials"` option in your repository configuration: +`"svn-cache-credentials"` option in your repository configuration: ```json { @@ -370,7 +370,7 @@ for this server will be overwritten. To change this behavior by setting the { "type": "vcs", "url": "http://svn.example.org/projectA/", - "cache-credentials": false + "svn-cache-credentials": false } ] } diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f2a9e1de0..729bda94e 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -67,8 +67,8 @@ class SvnDriver extends VcsDriver if (isset($this->repoConfig['tags-path'])) { $this->tagsPath = $this->repoConfig['tags-path']; } - if (array_key_exists('cache-credentials', $this->repoConfig)) { - $this->cacheCredentials = (bool) $this->repoConfig['cache-credentials']; + if (array_key_exists('svn-cache-credentials', $this->repoConfig)) { + $this->cacheCredentials = (bool) $this->repoConfig['svn-cache-credentials']; } if (isset($this->repoConfig['package-path'])) { $this->packagePath = '/' . trim($this->repoConfig['package-path'], '/'); From f3b0890cf48fe44882cc45b4a84b02889d5a9a92 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 24 Sep 2014 15:16:30 +0200 Subject: [PATCH 386/638] GitDownloader.php: better escaping for Windows Compability for Windows usernames and passwords. When usernames and passwords contain characters that are rawurlencoded to a string containing a % sign (e.g. @ becomes %40), the procent was replaced with a space. Git can there not authenticate. --- src/Composer/Downloader/GitDownloader.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 695996b5c..6c8b60dd9 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -48,7 +48,7 @@ class GitDownloader extends VcsDownloader $this->io->write(" Cloning ".$ref); $commandCallable = function ($url) use ($ref, $path, $command) { - return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref)); + return sprintf($command, $this->shellEscapeUrl ($url), escapeshellarg($path), escapeshellarg($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); @@ -61,6 +61,23 @@ class GitDownloader extends VcsDownloader $package->setSourceReference($newRef); } } + + /** + * Escape url. Usernames and password are rawurlencoded earlier in the process. So when the username contains a @ sign, + * it is escaped to %40. Windows replaces a % with a space., because the % sign is used for variables like %appdata%. To + * escape the % sign, one has to escape the % sign with a carat. + * + * http://windowsitpro.com/windows-server/how-can-i-pass-percent-sign-value-regexe + */ + + private function shellEscapeUrl ($url) { + $escapedUrl = escapeshellarg($url); + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $escapedUrl = str_replace('%','^%', $escapedUrl); + } + + return $escapedUrl; + } /** * {@inheritDoc} @@ -78,7 +95,7 @@ class GitDownloader extends VcsDownloader $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; $commandCallable = function ($url) use ($command) { - return sprintf($command, escapeshellarg($url)); + return sprintf($command, $this->shellEscapeUrl ($url)); }; $this->gitUtil->runCommand($commandCallable, $url, $path); From 6edabc629da47349b0591afaf1f41ae13f50ce27 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 24 Sep 2014 15:24:28 +0200 Subject: [PATCH 387/638] Update GitDownloader.php PHP 5.3. compat. --- src/Composer/Downloader/GitDownloader.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 6c8b60dd9..8feb5fcc8 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -46,9 +46,10 @@ class GitDownloader extends VcsDownloader $flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : ''; $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); - - $commandCallable = function ($url) use ($ref, $path, $command) { - return sprintf($command, $this->shellEscapeUrl ($url), escapeshellarg($path), escapeshellarg($ref)); + + $downloader = $this; + $commandCallable = function ($url) use ($ref, $path, $command, $downloader) { + return sprintf($command, $downloader->shellEscapeUrl ($url), escapeshellarg($path), escapeshellarg($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); @@ -94,8 +95,9 @@ class GitDownloader extends VcsDownloader $this->io->write(" Checking out ".$ref); $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; - $commandCallable = function ($url) use ($command) { - return sprintf($command, $this->shellEscapeUrl ($url)); + $downloader = $this; + $commandCallable = function ($url) use ($command, $downloader) { + return sprintf($command, $downloader->shellEscapeUrl ($url)); }; $this->gitUtil->runCommand($commandCallable, $url, $path); From 4c58abbb768c19610f0346b3f80c0dd9b20c6680 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 24 Sep 2014 15:27:31 +0200 Subject: [PATCH 388/638] Update GitDownloader.php --- src/Composer/Downloader/GitDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 8feb5fcc8..e87972214 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -71,7 +71,7 @@ class GitDownloader extends VcsDownloader * http://windowsitpro.com/windows-server/how-can-i-pass-percent-sign-value-regexe */ - private function shellEscapeUrl ($url) { + public function shellEscapeUrl ($url) { $escapedUrl = escapeshellarg($url); if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $escapedUrl = str_replace('%','^%', $escapedUrl); From 2612dc7c15ae6ec2bb678577516c5b567d653a5a Mon Sep 17 00:00:00 2001 From: Bastian Hofmann Date: Wed, 24 Sep 2014 15:30:48 +0200 Subject: [PATCH 389/638] fixed typo --- doc/05-repositories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 318ea65bb..975b473e5 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -345,7 +345,7 @@ If the package is in a sub-directory, e.g. `/trunk/foo/bar/composer.json` and setting the `"package-path"` option to the sub-directory, in this example it would be `"package-path": "foo/bar/"`. -If you have a private Subversion repository you can safe credentials in the +If you have a private Subversion repository you can save credentials in the http-basic section of your config (See [Schema](04-schema.md)): ```json From 7ae14306898e3ddeda09e0bec705cf57dade9809 Mon Sep 17 00:00:00 2001 From: frederik Date: Wed, 24 Sep 2014 17:18:15 +0200 Subject: [PATCH 390/638] #3297 ProcessUtil class using ProcessUtil of Symfony Console --- src/Composer/Command/HomeCommand.php | 3 +- src/Composer/Command/InitCommand.php | 3 +- src/Composer/Downloader/GitDownloader.php | 48 +++++++------------ src/Composer/Downloader/GzipDownloader.php | 3 +- src/Composer/Downloader/HgDownloader.php | 11 +++-- src/Composer/Downloader/RarDownloader.php | 3 +- src/Composer/Downloader/ZipDownloader.php | 3 +- .../EventDispatcher/EventDispatcher.php | 3 +- src/Composer/Installer/LibraryInstaller.php | 5 +- src/Composer/Installer/PearInstaller.php | 4 +- src/Composer/Package/Locker.php | 5 +- src/Composer/Repository/Vcs/GitDriver.php | 9 ++-- src/Composer/Repository/Vcs/HgDriver.php | 9 ++-- src/Composer/Util/Filesystem.php | 8 ++-- src/Composer/Util/ProcessUtil.php | 37 ++++++++++++++ src/Composer/Util/Svn.php | 8 ++-- 16 files changed, 98 insertions(+), 64 deletions(-) create mode 100644 src/Composer/Util/ProcessUtil.php diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 8bf68cac6..38538f461 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -23,6 +23,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\InvalidArgumentException; +use Composer\Util\ProcessUtil; /** * @author Robert Schönthal @@ -120,7 +121,7 @@ EOT */ private function openBrowser($url) { - $url = escapeshellarg($url); + $url = ProcessUtil::escapeArgument($url); if (defined('PHP_WINDOWS_VERSION_MAJOR')) { return passthru('start "web" explorer "' . $url . '"'); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index d7ee60677..ea292b9b8 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -20,6 +20,7 @@ use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Package\Version\VersionParser; +use Composer\Util\ProcessUtil; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -438,7 +439,7 @@ EOT $finder = new ExecutableFinder(); $gitBin = $finder->find('git'); - $cmd = new Process(sprintf('%s config -l', escapeshellarg($gitBin))); + $cmd = new Process(sprintf('%s config -l', ProcessUtil::escapeArgument($gitBin))); $cmd->run(); if ($cmd->isSuccessful()) { diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index e87972214..d319f143d 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -19,6 +19,7 @@ use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use Composer\Util\Filesystem; use Composer\Config; +use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -47,9 +48,12 @@ class GitDownloader extends VcsDownloader $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); - $downloader = $this; - $commandCallable = function ($url) use ($ref, $path, $command, $downloader) { - return sprintf($command, $downloader->shellEscapeUrl ($url), escapeshellarg($path), escapeshellarg($ref)); + $commandCallable = function ($url) use ($ref, $path, $command) { + return sprintf( + $command, + ProcessUtil::escapeArgument($url), + ProcessUtil::escapeArgument($path), + ProcessUtil::escapeArgument($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); @@ -62,23 +66,6 @@ class GitDownloader extends VcsDownloader $package->setSourceReference($newRef); } } - - /** - * Escape url. Usernames and password are rawurlencoded earlier in the process. So when the username contains a @ sign, - * it is escaped to %40. Windows replaces a % with a space., because the % sign is used for variables like %appdata%. To - * escape the % sign, one has to escape the % sign with a carat. - * - * http://windowsitpro.com/windows-server/how-can-i-pass-percent-sign-value-regexe - */ - - public function shellEscapeUrl ($url) { - $escapedUrl = escapeshellarg($url); - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $escapedUrl = str_replace('%','^%', $escapedUrl); - } - - return $escapedUrl; - } /** * {@inheritDoc} @@ -95,9 +82,8 @@ class GitDownloader extends VcsDownloader $this->io->write(" Checking out ".$ref); $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; - $downloader = $this; - $commandCallable = function ($url) use ($command, $downloader) { - return sprintf($command, $downloader->shellEscapeUrl ($url)); + $commandCallable = function ($url) use ($command) { + return sprintf($command, ProcessUtil::escapeArgument ($url)); }; $this->gitUtil->runCommand($commandCallable, $url, $path); @@ -244,7 +230,7 @@ class GitDownloader extends VcsDownloader && $branches && preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches) ) { - $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', escapeshellarg($branch), escapeshellarg('composer/'.$reference)); + $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessUtil::escapeArgument($branch), ProcessUtil::escapeArgument('composer/'.$reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -257,19 +243,19 @@ class GitDownloader extends VcsDownloader $branch = 'v' . $branch; } - $command = sprintf('git checkout %s', escapeshellarg($branch)); - $fallbackCommand = sprintf('git checkout -B %s %s', escapeshellarg($branch), escapeshellarg('composer/'.$branch)); + $command = sprintf('git checkout %s', ProcessUtil::escapeArgument($branch)); + $fallbackCommand = sprintf('git checkout -B %s %s', ProcessUtil::escapeArgument($branch), ProcessUtil::escapeArgument('composer/'.$branch)); if (0 === $this->process->execute($command, $output, $path) || 0 === $this->process->execute($fallbackCommand, $output, $path) ) { - $command = sprintf('git reset --hard %s', escapeshellarg($reference)); + $command = sprintf('git reset --hard %s', ProcessUtil::escapeArgument($reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } } } - $command = sprintf($template, escapeshellarg($gitRef)); + $command = sprintf($template, ProcessUtil::escapeArgument($gitRef)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -288,7 +274,7 @@ class GitDownloader extends VcsDownloader foreach ($this->process->splitLines($output) as $line) { if (preg_match('{^composer/'.preg_quote($branch).'(?:\.x)?$}i', trim($line))) { // find the previous commit by date in the given branch - if (0 === $this->process->execute(sprintf($guessTemplate, $date, escapeshellarg(trim($line))), $output, $path)) { + if (0 === $this->process->execute(sprintf($guessTemplate, $date, ProcessUtil::escapeArgument(trim($line))), $output, $path)) { $newReference = trim($output); } @@ -305,7 +291,7 @@ class GitDownloader extends VcsDownloader } // checkout the new recovered ref - $command = sprintf($template, escapeshellarg($newReference)); + $command = sprintf($template, ProcessUtil::escapeArgument($newReference)); if (0 === $this->process->execute($command, $output, $path)) { $this->io->write(' '.$reference.' is gone (history was rewritten?), recovered by checking out '.$newReference); @@ -325,7 +311,7 @@ class GitDownloader extends VcsDownloader if ($protocols[0] !== 'git') { $pushUrl = 'https://' . $match[1] . '/'.$match[2].'/'.$match[3].'.git'; } - $cmd = sprintf('git remote set-url --push origin %s', escapeshellarg($pushUrl)); + $cmd = sprintf('git remote set-url --push origin %s', ProcessUtil::escapeArgument($pushUrl)); $this->process->execute($cmd, $ignoredOutput, $path); } } diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 073b18c30..ab186be05 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -18,6 +18,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; +use Composer\Util\ProcessUtil; /** * GZip archive downloader. @@ -40,7 +41,7 @@ class GzipDownloader extends ArchiveDownloader // Try to use gunzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFilepath); + $command = 'gzip -cd ' . ProcessUtil::escapeArgument($file) . ' > ' . ProcessUtil::escapeArgument($targetFilepath); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 69e8ba886..835ad57eb 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -13,6 +13,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; +use Composer\Util\ProcessUtil; /** * @author Per Bernhardt @@ -24,10 +25,10 @@ class HgDownloader extends VcsDownloader */ public function doDownload(PackageInterface $package, $path, $url) { - $url = escapeshellarg($url); - $ref = escapeshellarg($package->getSourceReference()); + $url = ProcessUtil::escapeArgument($url); + $ref = ProcessUtil::escapeArgument($package->getSourceReference()); $this->io->write(" Cloning ".$package->getSourceReference()); - $command = sprintf('hg clone %s %s', $url, escapeshellarg($path)); + $command = sprintf('hg clone %s %s', $url, ProcessUtil::escapeArgument($path)); if (0 !== $this->process->execute($command, $ignoredOutput)) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } @@ -42,8 +43,8 @@ class HgDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $url = escapeshellarg($url); - $ref = escapeshellarg($target->getSourceReference()); + $url = ProcessUtil::escapeArgument($url); + $ref = ProcessUtil::escapeArgument($target->getSourceReference()); $this->io->write(" Updating to ".$target->getSourceReference()); if (!is_dir($path.'/.hg')) { diff --git a/src/Composer/Downloader/RarDownloader.php b/src/Composer/Downloader/RarDownloader.php index bb62ee0a8..a2980c22e 100644 --- a/src/Composer/Downloader/RarDownloader.php +++ b/src/Composer/Downloader/RarDownloader.php @@ -18,6 +18,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use RarArchive; +use Composer\Util\ProcessUtil; /** * RAR archive downloader. @@ -42,7 +43,7 @@ class RarDownloader extends ArchiveDownloader // Try to use unrar on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unrar x ' . escapeshellarg($file) . ' ' . escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path); + $command = 'unrar x ' . ProcessUtil::escapeArgument($file) . ' ' . ProcessUtil::escapeArgument($path) . ' && chmod -R u+w ' . ProcessUtil::escapeArgument($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index b35d6a5af..397f970c0 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -18,6 +18,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use ZipArchive; +use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -38,7 +39,7 @@ class ZipDownloader extends ArchiveDownloader // try to use unzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path); + $command = 'unzip '.ProcessUtil::escapeArgument($file).' -d '.ProcessUtil::escapeArgument($path) . ' && chmod -R u+w ' . ProcessUtil::escapeArgument($path); try { if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 7e882ddb3..44a3da2bd 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -19,6 +19,7 @@ use Composer\Script; use Composer\Script\CommandEvent; use Composer\Script\PackageEvent; use Composer\Util\ProcessExecutor; +use Composer\Util\ProcessUtil; /** * The Event Dispatcher. @@ -152,7 +153,7 @@ class EventDispatcher throw $e; } } else { - $args = implode(' ', array_map('escapeshellarg', $event->getArguments())); + $args = implode(' ', array_map(array('Composer\Util\ProcessUtil','escapeArgument'), $event->getArguments())); if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 4facfd494..2c37527d5 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\ProcessUtil; /** * Package installation manager. @@ -296,7 +297,7 @@ class LibraryInstaller implements InstallerInterface } return "@ECHO OFF\r\n". - "SET BIN_TARGET=%~dp0/".trim(escapeshellarg($binPath), '"')."\r\n". + "SET BIN_TARGET=%~dp0/".trim(ProcessUtil::escapeArgument($binPath), '"')."\r\n". "{$caller} \"%BIN_TARGET%\" %*\r\n"; } @@ -307,7 +308,7 @@ class LibraryInstaller implements InstallerInterface return "#!/usr/bin/env sh\n". 'SRC_DIR="`pwd`"'."\n". 'cd "`dirname "$0"`"'."\n". - 'cd '.escapeshellarg(dirname($binPath))."\n". + 'cd '.ProcessUtil::escapeArgument(dirname($binPath))."\n". 'BIN_TARGET="`pwd`/'.basename($binPath)."\"\n". 'cd "$SRC_DIR"'."\n". '"$BIN_TARGET" "$@"'."\n"; diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php index defadd9cf..bdd41b950 100644 --- a/src/Composer/Installer/PearInstaller.php +++ b/src/Composer/Installer/PearInstaller.php @@ -124,7 +124,7 @@ class PearInstaller extends LibraryInstaller "pushd .\r\n". "cd %~dp0\r\n". "set PHP_PROXY=%CD%\\composer-php.bat\r\n". - "cd ".escapeshellarg(dirname($binPath))."\r\n". + "cd ".ProcessUtil::escapeArgument(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". "%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n"; @@ -134,7 +134,7 @@ class PearInstaller extends LibraryInstaller return "@echo off\r\n". "pushd .\r\n". "cd %~dp0\r\n". - "cd ".escapeshellarg(dirname($binPath))."\r\n". + "cd ".ProcessUtil::escapeArgument(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". $caller." \"%BIN_TARGET%\" %*\r\n"; diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 2fa9b011a..159552172 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -22,6 +22,7 @@ use Composer\Package\Loader\ArrayLoader; use Composer\Package\Version\VersionParser; use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; +use Composer\Util\ProcessUtil; /** * Reads/writes project lockfile (composer.lock). @@ -341,13 +342,13 @@ class Locker case 'git': GitUtil::cleanEnv(); - if (0 === $this->process->execute('git log -n1 --pretty=%ct '.escapeshellarg($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { + if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessUtil::escapeArgument($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { $datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); } break; case 'hg': - if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.escapeshellarg($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { + if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.ProcessUtil::escapeArgument($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { $datetime = new \DateTime('@'.$match[1], new \DateTimeZone('UTC')); } break; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 20cfa93fc..eea945e1e 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -19,6 +19,7 @@ use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; use Composer\Cache; use Composer\Config; +use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -62,7 +63,7 @@ class GitDriver extends VcsDriver if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') { try { $commandCallable = function ($url) { - return sprintf('git remote set-url origin %s && git remote update --prune origin', escapeshellarg($url)); + return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessUtil::escapeArgument($url)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir); } catch (\Exception $e) { @@ -74,7 +75,7 @@ class GitDriver extends VcsDriver $repoDir = $this->repoDir; $commandCallable = function ($url) use ($repoDir) { - return sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($repoDir)); + return sprintf('git clone --mirror %s %s', ProcessUtil::escapeArgument($url), ProcessUtil::escapeArgument($repoDir)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true); @@ -147,7 +148,7 @@ class GitDriver extends VcsDriver } if (!isset($this->infoCache[$identifier])) { - $resource = sprintf('%s:composer.json', escapeshellarg($identifier)); + $resource = sprintf('%s:composer.json', ProcessUtil::escapeArgument($identifier)); $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir); if (!trim($composer)) { @@ -157,7 +158,7 @@ class GitDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $this->process->execute(sprintf('git log -1 --format=%%at %s', escapeshellarg($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessUtil::escapeArgument($identifier)), $output, $this->repoDir); $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 7b9d1ff8c..1fba69f10 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -17,6 +17,7 @@ use Composer\Json\JsonFile; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; use Composer\IO\IOInterface; +use Composer\Util\ProcessUtil; /** * @author Per Bernhardt @@ -56,7 +57,7 @@ class HgDriver extends VcsDriver // clean up directory and do a fresh clone into it $fs->removeDirectory($this->repoDir); - if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', escapeshellarg($this->url), escapeshellarg($this->repoDir)), $output, $cacheDir)) { + if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', ProcessUtil::escapeArgument($this->url), ProcessUtil::escapeArgument($this->repoDir)), $output, $cacheDir)) { $output = $this->process->getErrorOutput(); if (0 !== $this->process->execute('hg --version', $ignoredOutput)) { @@ -116,7 +117,7 @@ class HgDriver extends VcsDriver public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $this->process->execute(sprintf('hg cat -r %s composer.json', escapeshellarg($identifier)), $composer, $this->repoDir); + $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessUtil::escapeArgument($identifier)), $composer, $this->repoDir); if (!trim($composer)) { return; @@ -125,7 +126,7 @@ class HgDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $identifier); if (!isset($composer['time'])) { - $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', escapeshellarg($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessUtil::escapeArgument($identifier)), $output, $this->repoDir); $date = new \DateTime(trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } @@ -215,7 +216,7 @@ class HgDriver extends VcsDriver } $processExecutor = new ProcessExecutor(); - $exit = $processExecutor->execute(sprintf('hg identify %s', escapeshellarg($url)), $ignored); + $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessUtil::escapeArgument($url)), $ignored); return $exit === 0; } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 6044c25d6..9ed24c503 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -112,9 +112,9 @@ class Filesystem } if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $cmd = sprintf('rmdir /S /Q %s', escapeshellarg(realpath($directory))); + $cmd = sprintf('rmdir /S /Q %s', ProcessUtil::escapeArgument(realpath($directory))); } else { - $cmd = sprintf('rm -rf %s', escapeshellarg($directory)); + $cmd = sprintf('rm -rf %s', ProcessUtil::escapeArgument($directory)); } $result = $this->getProcess()->execute($cmd, $output) === 0; @@ -269,7 +269,7 @@ class Filesystem if (defined('PHP_WINDOWS_VERSION_BUILD')) { // Try to copy & delete - this is a workaround for random "Access denied" errors. - $command = sprintf('xcopy %s %s /E /I /Q', escapeshellarg($source), escapeshellarg($target)); + $command = sprintf('xcopy %s %s /E /I /Q', ProcessUtil::escapeArgument($source), ProcessUtil::escapeArgument($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache @@ -283,7 +283,7 @@ class Filesystem } else { // We do not use PHP's "rename" function here since it does not support // the case where $source, and $target are located on different partitions. - $command = sprintf('mv %s %s', escapeshellarg($source), escapeshellarg($target)); + $command = sprintf('mv %s %s', ProcessUtil::escapeArgument($source), ProcessUtil::escapeArgument($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache diff --git a/src/Composer/Util/ProcessUtil.php b/src/Composer/Util/ProcessUtil.php new file mode 100644 index 000000000..75b6fd9df --- /dev/null +++ b/src/Composer/Util/ProcessUtil.php @@ -0,0 +1,37 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Symfony\Component\Process\ProcessUtils; + +/** + * @author Frederik Bosch + */ + +class ProcessUtil +{ + + /** + * Escapes a string to be used as a shell argument. + * + * @param string $argument The argument that will be escaped + * + * @return string The escaped argument + */ + public static function escapeArgument ($argument) + { + return ProcessUtils::escapeArgument($argument); + } + + +} diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 4ec13297f..e6d370abf 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -188,11 +188,11 @@ class Svn $cmd, '--non-interactive ', $this->getCredentialString(), - escapeshellarg($url) + ProcessUtil::escapeArgument($url) ); if ($path) { - $cmd .= ' ' . escapeshellarg($path); + $cmd .= ' ' . ProcessUtil::escapeArgument($path); } return $cmd; @@ -214,8 +214,8 @@ class Svn return sprintf( ' %s--username %s --password %s ', $this->getAuthCache(), - escapeshellarg($this->getUsername()), - escapeshellarg($this->getPassword()) + ProcessUtil::escapeArgument($this->getUsername()), + ProcessUtil::escapeArgument($this->getPassword()) ); } From d1d40502bf956a45c937cd61b0f65f3b90652e14 Mon Sep 17 00:00:00 2001 From: frederik Date: Wed, 24 Sep 2014 18:30:12 +0200 Subject: [PATCH 391/638] ProcessExecutor::escape --- src/Composer/Command/HomeCommand.php | 4 +- src/Composer/Command/InitCommand.php | 4 +- src/Composer/Downloader/GitDownloader.php | 25 ++++++------- src/Composer/Downloader/GzipDownloader.php | 3 +- src/Composer/Downloader/HgDownloader.php | 12 +++--- src/Composer/Downloader/RarDownloader.php | 3 +- src/Composer/Downloader/ZipDownloader.php | 3 +- .../EventDispatcher/EventDispatcher.php | 3 +- src/Composer/Installer/LibraryInstaller.php | 6 +-- src/Composer/Installer/PearInstaller.php | 5 ++- src/Composer/Package/Locker.php | 5 +-- src/Composer/Repository/Vcs/GitDriver.php | 9 ++--- src/Composer/Repository/Vcs/HgDriver.php | 9 ++--- src/Composer/Util/Filesystem.php | 8 ++-- src/Composer/Util/ProcessExecutor.php | 14 +++++++ src/Composer/Util/ProcessUtil.php | 37 ------------------- src/Composer/Util/Svn.php | 8 ++-- 17 files changed, 64 insertions(+), 94 deletions(-) delete mode 100644 src/Composer/Util/ProcessUtil.php diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 38538f461..023fb0843 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -18,12 +18,12 @@ use Composer\Package\CompletePackageInterface; use Composer\Package\Loader\InvalidPackageException; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryInterface; +use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\InvalidArgumentException; -use Composer\Util\ProcessUtil; /** * @author Robert Schönthal @@ -121,7 +121,7 @@ EOT */ private function openBrowser($url) { - $url = ProcessUtil::escapeArgument($url); + $url = ProcessExecutor::escape($url); if (defined('PHP_WINDOWS_VERSION_MAJOR')) { return passthru('start "web" explorer "' . $url . '"'); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index ea292b9b8..8aa734630 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -20,7 +20,7 @@ use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Package\Version\VersionParser; -use Composer\Util\ProcessUtil; +use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -439,7 +439,7 @@ EOT $finder = new ExecutableFinder(); $gitBin = $finder->find('git'); - $cmd = new Process(sprintf('%s config -l', ProcessUtil::escapeArgument($gitBin))); + $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin))); $cmd->run(); if ($cmd->isSuccessful()) { diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index d319f143d..cbd7b0ba6 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -19,7 +19,6 @@ use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use Composer\Util\Filesystem; use Composer\Config; -use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -51,9 +50,9 @@ class GitDownloader extends VcsDownloader $commandCallable = function ($url) use ($ref, $path, $command) { return sprintf( $command, - ProcessUtil::escapeArgument($url), - ProcessUtil::escapeArgument($path), - ProcessUtil::escapeArgument($ref)); + ProcessExecutor::escape($url), + ProcessExecutor::escape($path), + ProcessExecutor::escape($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); @@ -83,7 +82,7 @@ class GitDownloader extends VcsDownloader $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; $commandCallable = function ($url) use ($command) { - return sprintf($command, ProcessUtil::escapeArgument ($url)); + return sprintf($command, ProcessExecutor::escape ($url)); }; $this->gitUtil->runCommand($commandCallable, $url, $path); @@ -230,7 +229,7 @@ class GitDownloader extends VcsDownloader && $branches && preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches) ) { - $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessUtil::escapeArgument($branch), ProcessUtil::escapeArgument('composer/'.$reference)); + $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -243,19 +242,19 @@ class GitDownloader extends VcsDownloader $branch = 'v' . $branch; } - $command = sprintf('git checkout %s', ProcessUtil::escapeArgument($branch)); - $fallbackCommand = sprintf('git checkout -B %s %s', ProcessUtil::escapeArgument($branch), ProcessUtil::escapeArgument('composer/'.$branch)); + $command = sprintf('git checkout %s', ProcessExecutor::escape($branch)); + $fallbackCommand = sprintf('git checkout -B %s %s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch)); if (0 === $this->process->execute($command, $output, $path) || 0 === $this->process->execute($fallbackCommand, $output, $path) ) { - $command = sprintf('git reset --hard %s', ProcessUtil::escapeArgument($reference)); + $command = sprintf('git reset --hard %s', ProcessExecutor::escape($reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } } } - $command = sprintf($template, ProcessUtil::escapeArgument($gitRef)); + $command = sprintf($template, ProcessExecutor::escape($gitRef)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -274,7 +273,7 @@ class GitDownloader extends VcsDownloader foreach ($this->process->splitLines($output) as $line) { if (preg_match('{^composer/'.preg_quote($branch).'(?:\.x)?$}i', trim($line))) { // find the previous commit by date in the given branch - if (0 === $this->process->execute(sprintf($guessTemplate, $date, ProcessUtil::escapeArgument(trim($line))), $output, $path)) { + if (0 === $this->process->execute(sprintf($guessTemplate, $date, ProcessExecutor::escape(trim($line))), $output, $path)) { $newReference = trim($output); } @@ -291,7 +290,7 @@ class GitDownloader extends VcsDownloader } // checkout the new recovered ref - $command = sprintf($template, ProcessUtil::escapeArgument($newReference)); + $command = sprintf($template, ProcessExecutor::escape($newReference)); if (0 === $this->process->execute($command, $output, $path)) { $this->io->write(' '.$reference.' is gone (history was rewritten?), recovered by checking out '.$newReference); @@ -311,7 +310,7 @@ class GitDownloader extends VcsDownloader if ($protocols[0] !== 'git') { $pushUrl = 'https://' . $match[1] . '/'.$match[2].'/'.$match[3].'.git'; } - $cmd = sprintf('git remote set-url --push origin %s', ProcessUtil::escapeArgument($pushUrl)); + $cmd = sprintf('git remote set-url --push origin %s', ProcessExecutor::escape($pushUrl)); $this->process->execute($cmd, $ignoredOutput, $path); } } diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index ab186be05..f8624ab24 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -18,7 +18,6 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; -use Composer\Util\ProcessUtil; /** * GZip archive downloader. @@ -41,7 +40,7 @@ class GzipDownloader extends ArchiveDownloader // Try to use gunzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'gzip -cd ' . ProcessUtil::escapeArgument($file) . ' > ' . ProcessUtil::escapeArgument($targetFilepath); + $command = 'gzip -cd ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 835ad57eb..3d5cc6209 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -13,7 +13,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; -use Composer\Util\ProcessUtil; +use Composer\Util\ProcessExecutor; /** * @author Per Bernhardt @@ -25,10 +25,10 @@ class HgDownloader extends VcsDownloader */ public function doDownload(PackageInterface $package, $path, $url) { - $url = ProcessUtil::escapeArgument($url); - $ref = ProcessUtil::escapeArgument($package->getSourceReference()); + $url = ProcessExecutor::escape($url); + $ref = ProcessExecutor::escape($package->getSourceReference()); $this->io->write(" Cloning ".$package->getSourceReference()); - $command = sprintf('hg clone %s %s', $url, ProcessUtil::escapeArgument($path)); + $command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path)); if (0 !== $this->process->execute($command, $ignoredOutput)) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } @@ -43,8 +43,8 @@ class HgDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $url = ProcessUtil::escapeArgument($url); - $ref = ProcessUtil::escapeArgument($target->getSourceReference()); + $url = ProcessExecutor::escape($url); + $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->write(" Updating to ".$target->getSourceReference()); if (!is_dir($path.'/.hg')) { diff --git a/src/Composer/Downloader/RarDownloader.php b/src/Composer/Downloader/RarDownloader.php index a2980c22e..12823422d 100644 --- a/src/Composer/Downloader/RarDownloader.php +++ b/src/Composer/Downloader/RarDownloader.php @@ -18,7 +18,6 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use RarArchive; -use Composer\Util\ProcessUtil; /** * RAR archive downloader. @@ -43,7 +42,7 @@ class RarDownloader extends ArchiveDownloader // Try to use unrar on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unrar x ' . ProcessUtil::escapeArgument($file) . ' ' . ProcessUtil::escapeArgument($path) . ' && chmod -R u+w ' . ProcessUtil::escapeArgument($path); + $command = 'unrar x ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 397f970c0..1370d82af 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -18,7 +18,6 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use ZipArchive; -use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -39,7 +38,7 @@ class ZipDownloader extends ArchiveDownloader // try to use unzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unzip '.ProcessUtil::escapeArgument($file).' -d '.ProcessUtil::escapeArgument($path) . ' && chmod -R u+w ' . ProcessUtil::escapeArgument($path); + $command = 'unzip '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path); try { if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 44a3da2bd..620010494 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -19,7 +19,6 @@ use Composer\Script; use Composer\Script\CommandEvent; use Composer\Script\PackageEvent; use Composer\Util\ProcessExecutor; -use Composer\Util\ProcessUtil; /** * The Event Dispatcher. @@ -153,7 +152,7 @@ class EventDispatcher throw $e; } } else { - $args = implode(' ', array_map(array('Composer\Util\ProcessUtil','escapeArgument'), $event->getArguments())); + $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments())); if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 2c37527d5..05cd420d7 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -17,7 +17,7 @@ use Composer\IO\IOInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; use Composer\Util\Filesystem; -use Composer\Util\ProcessUtil; +use Composer\Util\ProcessExecutor; /** * Package installation manager. @@ -297,7 +297,7 @@ class LibraryInstaller implements InstallerInterface } return "@ECHO OFF\r\n". - "SET BIN_TARGET=%~dp0/".trim(ProcessUtil::escapeArgument($binPath), '"')."\r\n". + "SET BIN_TARGET=%~dp0/".trim(ProcessExecutor::escape($binPath), '"')."\r\n". "{$caller} \"%BIN_TARGET%\" %*\r\n"; } @@ -308,7 +308,7 @@ class LibraryInstaller implements InstallerInterface return "#!/usr/bin/env sh\n". 'SRC_DIR="`pwd`"'."\n". 'cd "`dirname "$0"`"'."\n". - 'cd '.ProcessUtil::escapeArgument(dirname($binPath))."\n". + 'cd '.ProcessExecutor::escape(dirname($binPath))."\n". 'BIN_TARGET="`pwd`/'.basename($binPath)."\"\n". 'cd "$SRC_DIR"'."\n". '"$BIN_TARGET" "$@"'."\n"; diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php index bdd41b950..fd3bbd976 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\ProcessExecutor; /** * Package installation manager. @@ -124,7 +125,7 @@ class PearInstaller extends LibraryInstaller "pushd .\r\n". "cd %~dp0\r\n". "set PHP_PROXY=%CD%\\composer-php.bat\r\n". - "cd ".ProcessUtil::escapeArgument(dirname($binPath))."\r\n". + "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". "%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n"; @@ -134,7 +135,7 @@ class PearInstaller extends LibraryInstaller return "@echo off\r\n". "pushd .\r\n". "cd %~dp0\r\n". - "cd ".ProcessUtil::escapeArgument(dirname($binPath))."\r\n". + "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". $caller." \"%BIN_TARGET%\" %*\r\n"; diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 159552172..4eb836706 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -22,7 +22,6 @@ use Composer\Package\Loader\ArrayLoader; use Composer\Package\Version\VersionParser; use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; -use Composer\Util\ProcessUtil; /** * Reads/writes project lockfile (composer.lock). @@ -342,13 +341,13 @@ class Locker case 'git': GitUtil::cleanEnv(); - if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessUtil::escapeArgument($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { + if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { $datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); } break; case 'hg': - if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.ProcessUtil::escapeArgument($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { + if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { $datetime = new \DateTime('@'.$match[1], new \DateTimeZone('UTC')); } break; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index eea945e1e..013817a5c 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -19,7 +19,6 @@ use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; use Composer\Cache; use Composer\Config; -use Composer\Util\ProcessUtil; /** * @author Jordi Boggiano @@ -63,7 +62,7 @@ class GitDriver extends VcsDriver if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') { try { $commandCallable = function ($url) { - return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessUtil::escapeArgument($url)); + return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessExecutor::escape($url)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir); } catch (\Exception $e) { @@ -75,7 +74,7 @@ class GitDriver extends VcsDriver $repoDir = $this->repoDir; $commandCallable = function ($url) use ($repoDir) { - return sprintf('git clone --mirror %s %s', ProcessUtil::escapeArgument($url), ProcessUtil::escapeArgument($repoDir)); + return sprintf('git clone --mirror %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($repoDir)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true); @@ -148,7 +147,7 @@ class GitDriver extends VcsDriver } if (!isset($this->infoCache[$identifier])) { - $resource = sprintf('%s:composer.json', ProcessUtil::escapeArgument($identifier)); + $resource = sprintf('%s:composer.json', ProcessExecutor::escape($identifier)); $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir); if (!trim($composer)) { @@ -158,7 +157,7 @@ class GitDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessUtil::escapeArgument($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 1fba69f10..77a8f2d86 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -17,7 +17,6 @@ use Composer\Json\JsonFile; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; use Composer\IO\IOInterface; -use Composer\Util\ProcessUtil; /** * @author Per Bernhardt @@ -57,7 +56,7 @@ class HgDriver extends VcsDriver // clean up directory and do a fresh clone into it $fs->removeDirectory($this->repoDir); - if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', ProcessUtil::escapeArgument($this->url), ProcessUtil::escapeArgument($this->repoDir)), $output, $cacheDir)) { + if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($this->url), ProcessExecutor::escape($this->repoDir)), $output, $cacheDir)) { $output = $this->process->getErrorOutput(); if (0 !== $this->process->execute('hg --version', $ignoredOutput)) { @@ -117,7 +116,7 @@ class HgDriver extends VcsDriver public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessUtil::escapeArgument($identifier)), $composer, $this->repoDir); + $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessExecutor::escape($identifier)), $composer, $this->repoDir); if (!trim($composer)) { return; @@ -126,7 +125,7 @@ class HgDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $identifier); if (!isset($composer['time'])) { - $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessUtil::escapeArgument($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime(trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } @@ -216,7 +215,7 @@ class HgDriver extends VcsDriver } $processExecutor = new ProcessExecutor(); - $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessUtil::escapeArgument($url)), $ignored); + $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored); return $exit === 0; } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 9ed24c503..cc83155e7 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -112,9 +112,9 @@ class Filesystem } if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $cmd = sprintf('rmdir /S /Q %s', ProcessUtil::escapeArgument(realpath($directory))); + $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory))); } else { - $cmd = sprintf('rm -rf %s', ProcessUtil::escapeArgument($directory)); + $cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory)); } $result = $this->getProcess()->execute($cmd, $output) === 0; @@ -269,7 +269,7 @@ class Filesystem if (defined('PHP_WINDOWS_VERSION_BUILD')) { // Try to copy & delete - this is a workaround for random "Access denied" errors. - $command = sprintf('xcopy %s %s /E /I /Q', ProcessUtil::escapeArgument($source), ProcessUtil::escapeArgument($target)); + $command = sprintf('xcopy %s %s /E /I /Q', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache @@ -283,7 +283,7 @@ class Filesystem } else { // We do not use PHP's "rename" function here since it does not support // the case where $source, and $target are located on different partitions. - $command = sprintf('mv %s %s', ProcessUtil::escapeArgument($source), ProcessUtil::escapeArgument($target)); + $command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index ab4e4b2db..d5a0549d3 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -13,6 +13,7 @@ namespace Composer\Util; use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessUtils; use Composer\IO\IOInterface; /** @@ -104,4 +105,17 @@ class ProcessExecutor { static::$timeout = $timeout; } + + /** + * Escapes a string to be used as a shell argument. + * + * @param string $argument The argument that will be escaped + * + * @return string The escaped argument + */ + + public static function escape ($argument) + { + return ProcessUtils::escapeArgument($argument); + } } diff --git a/src/Composer/Util/ProcessUtil.php b/src/Composer/Util/ProcessUtil.php deleted file mode 100644 index 75b6fd9df..000000000 --- a/src/Composer/Util/ProcessUtil.php +++ /dev/null @@ -1,37 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Util; - -use Symfony\Component\Process\ProcessUtils; - -/** - * @author Frederik Bosch - */ - -class ProcessUtil -{ - - /** - * Escapes a string to be used as a shell argument. - * - * @param string $argument The argument that will be escaped - * - * @return string The escaped argument - */ - public static function escapeArgument ($argument) - { - return ProcessUtils::escapeArgument($argument); - } - - -} diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index e6d370abf..56c96aab5 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -188,11 +188,11 @@ class Svn $cmd, '--non-interactive ', $this->getCredentialString(), - ProcessUtil::escapeArgument($url) + ProcessExecutor::escape($url) ); if ($path) { - $cmd .= ' ' . ProcessUtil::escapeArgument($path); + $cmd .= ' ' . ProcessExecutor::escape($path); } return $cmd; @@ -214,8 +214,8 @@ class Svn return sprintf( ' %s--username %s --password %s ', $this->getAuthCache(), - ProcessUtil::escapeArgument($this->getUsername()), - ProcessUtil::escapeArgument($this->getPassword()) + ProcessExecutor::escape($this->getUsername()), + ProcessExecutor::escape($this->getPassword()) ); } From 55a6a1c3d4a968233fb04a4c579f6c4429bcfe31 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Sep 2014 18:30:53 +0100 Subject: [PATCH 392/638] Add support for nested arrays in the json manipulator, fixes #3296 --- src/Composer/Json/JsonManipulator.php | 6 +- .../Test/Json/JsonManipulatorTest.php | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index e7c3e1459..b64ba7a95 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -18,6 +18,7 @@ namespace Composer\Json; class JsonManipulator { private static $RECURSE_BLOCKS; + private static $RECURSE_ARRAYS; private static $JSON_VALUE; private static $JSON_STRING; @@ -29,8 +30,9 @@ class JsonManipulator { if (!self::$RECURSE_BLOCKS) { self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*'; - self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"'; - self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\[[^\]]*\]|\{'.self::$RECURSE_BLOCKS.'\})'; + self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\])*'; + self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])+"'; + self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})'; } $contents = trim($contents); diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 9dd8c653d..1a0f976b5 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -228,6 +228,54 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase "foo": "qux" } } +' + ), + array( + '{ + "repositories": [{ + "type": "package", + "package": { + "bar": "ba[z", + "dist": { + "url": "http...", + "type": "zip" + }, + "autoload": { + "classmap": [ "foo/bar" ] + } + } + }], + "require": { + "php": "5.*" + }, + "require-dev": { + "foo": "bar" + } +}', + 'require-dev', + 'foo', + 'qux', + '{ + "repositories": [{ + "type": "package", + "package": { + "bar": "ba[z", + "dist": { + "url": "http...", + "type": "zip" + }, + "autoload": { + "classmap": [ "foo/bar" ] + } + } + }], + "require": { + "php": "5.*" + }, + "require-dev": { + "foo": "qux" + } +} ' ), ); @@ -468,6 +516,24 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase "package": { "bar": "ba}z" } } } +}', + 'bar', + false + ), + 'fails on deep arrays with borked texts' => array( + '{ + "repositories": [{ + "package": { "bar": "ba[z" } + }] +}', + 'bar', + false + ), + 'fails on deep arrays with borked texts2' => array( + '{ + "repositories": [{ + "package": { "bar": "ba]z" } + }] }', 'bar', false From 91ac3e1426b5ac9cc8e0636354b3c71b6f2f10cd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Sep 2014 19:13:05 +0100 Subject: [PATCH 393/638] Add support for unixy paths in git/hg local repo urls, closes #3294 --- src/Composer/Repository/Vcs/GitDriver.php | 4 ++-- src/Composer/Repository/Vcs/HgDriver.php | 2 +- src/Composer/Repository/Vcs/VcsDriver.php | 2 +- src/Composer/Util/Filesystem.php | 9 +++++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 20cfa93fc..180c441c7 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -225,12 +225,12 @@ class GitDriver extends VcsDriver // local filesystem if (Filesystem::isLocalPath($url)) { + $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: '.$url); } - $process = new ProcessExecutor(); - $url = str_replace('file://', '', $url); + $process = new ProcessExecutor($io); // check whether there is a git repo in that path if ($process->execute('git tag', $output, $url) === 0) { return true; diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 7b9d1ff8c..b72058790 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -198,12 +198,12 @@ class HgDriver extends VcsDriver // local filesystem if (Filesystem::isLocalPath($url)) { + $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { throw new \RuntimeException('Directory does not exist: '.$url); } $process = new ProcessExecutor(); - $url = str_replace('file://', '', $url); // check whether there is a hg repo in that path if ($process->execute('hg summary', $output, $url) === 0) { return true; diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 262309e67..b03f55684 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -46,7 +46,7 @@ abstract class VcsDriver implements VcsDriverInterface final public function __construct(array $repoConfig, IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) { if (Filesystem::isLocalPath($repoConfig['url'])) { - $repoConfig['url'] = preg_replace('{^file://}', '', $repoConfig['url']); + $repoConfig['url'] = Filesystem::getPlatformPath($repoConfig['url']); } $this->url = $repoConfig['url']; diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 6044c25d6..984f91e62 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -461,6 +461,15 @@ class Filesystem return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/]|\.\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i', $path); } + public static function getPlatformPath($path) + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $path = preg_replace('{^(?:file:///([a-z])/)}i', 'file://$1:/', $path); + } + + return preg_replace('{^file://}i', '', $path); + } + protected function directorySize($directory) { $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); From 4e774e8b2cba4e34a19a0fcb5824e1ad17ec73fb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Sep 2014 19:17:54 +0100 Subject: [PATCH 394/638] Fix formatting --- src/Composer/Downloader/GitDownloader.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index cbd7b0ba6..9661fabe0 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -46,13 +46,9 @@ class GitDownloader extends VcsDownloader $flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : ''; $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); - + $commandCallable = function ($url) use ($ref, $path, $command) { - return sprintf( - $command, - ProcessExecutor::escape($url), - ProcessExecutor::escape($path), - ProcessExecutor::escape($ref)); + return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); From 69afedb49c48239e4bb7219ec1bc2e1ee5b83595 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Sep 2014 19:19:25 +0100 Subject: [PATCH 395/638] Update deps --- composer.lock | 114 +++++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/composer.lock b/composer.lock index a20854ecc..b3a509716 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "justinrainbow/json-schema", - "version": "1.3.6", + "version": "1.3.7", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "d97cf3ce890fe80f247fc08594a1c8a1029fc7ed" + "reference": "87b54b460febed69726c781ab67462084e97a105" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/d97cf3ce890fe80f247fc08594a1c8a1029fc7ed", - "reference": "d97cf3ce890fe80f247fc08594a1c8a1029fc7ed", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/87b54b460febed69726c781ab67462084e97a105", + "reference": "87b54b460febed69726c781ab67462084e97a105", "shasum": "" }, "require": { @@ -47,11 +47,6 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch", - "homepage": "http://wiedler.ch/igor/" - }, { "name": "Bruno Prieto Reis", "email": "bruno.p.reis@gmail.com" @@ -60,10 +55,13 @@ "name": "Justin Rainbow", "email": "justin.rainbow@gmail.com" }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, { "name": "Robert Schönthal", - "email": "seroscho@googlemail.com", - "homepage": "http://digitalkaoz.net" + "email": "seroscho@googlemail.com" } ], "description": "A library to validate a json schema.", @@ -72,7 +70,7 @@ "json", "schema" ], - "time": "2014-03-05 15:03:52" + "time": "2014-08-25 02:48:14" }, { "name": "seld/jsonlint", @@ -122,17 +120,17 @@ }, { "name": "symfony/console", - "version": "v2.5.2", + "version": "v2.5.4", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "386fa63407805959bd2c5fe540294721ad4224c8" + "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/386fa63407805959bd2c5fe540294721ad4224c8", - "reference": "386fa63407805959bd2c5fe540294721ad4224c8", + "url": "https://api.github.com/repos/symfony/Console/zipball/748beed2a1e73179c3f5154d33fe6ae100c1aeb1", + "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1", "shasum": "" }, "require": { @@ -162,34 +160,32 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-07-15 14:15:12" + "time": "2014-08-14 16:10:54" }, { "name": "symfony/finder", - "version": "v2.5.2", + "version": "v2.5.4", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", "url": "https://github.com/symfony/Finder.git", - "reference": "576d8f69feec477067e91b6bd0367c113e76a1a0" + "reference": "f40854d1a19c339c7f969f8f6d6d6e9153311c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/576d8f69feec477067e91b6bd0367c113e76a1a0", - "reference": "576d8f69feec477067e91b6bd0367c113e76a1a0", + "url": "https://api.github.com/repos/symfony/Finder/zipball/f40854d1a19c339c7f969f8f6d6d6e9153311c4c", + "reference": "f40854d1a19c339c7f969f8f6d6d6e9153311c4c", "shasum": "" }, "require": { @@ -211,34 +207,32 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Finder Component", "homepage": "http://symfony.com", - "time": "2014-07-15 14:15:12" + "time": "2014-09-03 09:00:14" }, { "name": "symfony/process", - "version": "v2.5.2", + "version": "v2.5.4", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "5e53efbf61a7fbf73c79e3e08feea50f64c20bfa" + "reference": "136cf0bdaacea81f779583376d47dd8aef4fc6ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/5e53efbf61a7fbf73c79e3e08feea50f64c20bfa", - "reference": "5e53efbf61a7fbf73c79e3e08feea50f64c20bfa", + "url": "https://api.github.com/repos/symfony/Process/zipball/136cf0bdaacea81f779583376d47dd8aef4fc6ba", + "reference": "136cf0bdaacea81f779583376d47dd8aef4fc6ba", "shasum": "" }, "require": { @@ -260,42 +254,40 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-07-09 09:05:48" + "time": "2014-08-31 03:22:04" } ], "packages-dev": [ { "name": "phpunit/php-code-coverage", - "version": "1.2.17", + "version": "1.2.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34" + "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", - "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", + "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", "shasum": "" }, "require": { "php": ">=5.3.3", "phpunit/php-file-iterator": ">=1.3.0@stable", "phpunit/php-text-template": ">=1.2.0@stable", - "phpunit/php-token-stream": ">=1.1.3@stable" + "phpunit/php-token-stream": ">=1.1.3,<1.3.0" }, "require-dev": { "phpunit/phpunit": "3.7.*@dev" @@ -336,7 +328,7 @@ "testing", "xunit" ], - "time": "2014-03-28 10:53:45" + "time": "2014-09-02 10:13:14" }, { "name": "phpunit/php-file-iterator", @@ -645,17 +637,17 @@ }, { "name": "symfony/yaml", - "version": "v2.5.2", + "version": "v2.5.4", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1" + "reference": "01a7695bcfb013d0a15c6757e15aae120342986f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", - "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/01a7695bcfb013d0a15c6757e15aae120342986f", + "reference": "01a7695bcfb013d0a15c6757e15aae120342986f", "shasum": "" }, "require": { @@ -677,20 +669,18 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, { "name": "Symfony Community", "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" } ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-07-09 09:05:48" + "time": "2014-08-31 03:22:04" } ], "aliases": [ From 1bc6158cd85160d0aba8543594db6fc2f948f207 Mon Sep 17 00:00:00 2001 From: Luis Cordova Date: Thu, 25 Sep 2014 08:27:10 -0500 Subject: [PATCH 396/638] note special case for ~1 cases referenced from https://github.com/symfony/symfony-docs/pull/4267/files#r18030609 --- doc/01-basic-usage.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 803032606..5d703bd38 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -94,6 +94,11 @@ breaks until 2.0, that works well. Another way of looking at it is that using > like `~1.2` would not install it. As said above `~1.2` only means the `.2` > can change but the `1.` part is fixed. +> **Note:** The `~` operator has an exception on its behavior for the major +> release number. This means for example that `~1` is the same as `~1.0` as +> it will not allow the major number to increase trying to keep backwards +> compatibility. + ### Stability By default only stable releases are taken into consideration. If you would like From 43b9ef309e7989e0fcf7e8436dbd639ae02506c7 Mon Sep 17 00:00:00 2001 From: Chris Harvey Date: Thu, 25 Sep 2014 18:39:50 +0100 Subject: [PATCH 397/638] Adding missing closing tag when re-applying stashed changes --- src/Composer/Downloader/GitDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 9661fabe0..0584f675a 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -191,7 +191,7 @@ class GitDownloader extends VcsDownloader $path = $this->normalizePath($path); if ($this->hasStashedChanges) { $this->hasStashedChanges = false; - $this->io->write(' Re-applying stashed changes'); + $this->io->write(' Re-applying stashed changes'); if (0 !== $this->process->execute('git stash pop', $output, $path)) { throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput()); } From fc986d76c8b77c5d2160009c698736e395bb436f Mon Sep 17 00:00:00 2001 From: Ala Eddine Khefifi Date: Mon, 29 Sep 2014 10:40:29 +0100 Subject: [PATCH 398/638] Update 01-basic-usage.md --- doc/01-basic-usage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 5d703bd38..715748d8a 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -149,7 +149,8 @@ dependencies installed are still working even if your dependencies released many new versions since then. If no `composer.lock` file exists, Composer will read the dependencies and -versions from `composer.json` and create the lock file. +versions from `composer.json` and create the lock file after executing the `update` or the `install` +command. This means that if any of the dependencies get a new version, you won't get the updates automatically. To update to the new version, use `update` command. This will fetch From 03ee6491471b95114bc13d4decbd779fb3efbf73 Mon Sep 17 00:00:00 2001 From: Ala Eddine Khefifi Date: Mon, 29 Sep 2014 11:01:02 +0100 Subject: [PATCH 399/638] Update 01-basic-usage.md --- doc/01-basic-usage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 5d703bd38..4705ce85d 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -159,7 +159,9 @@ the lock file with the new version. ```sh php composer.phar update ``` - +> **Note:** Composer will display a Warning when executing an `install` command if + `composer.lock` and `composer.json` are not synchronized. + If you only want to install or update one dependency, you can whitelist them: ```sh From 1cbd1a7ba8b72f53b1bfc49c97eef7987d6522d9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 00:37:28 +0100 Subject: [PATCH 400/638] Examplify chars and reword, closes #3212 --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index c90ce6cf1..eb5921406 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges, separated by a comma, which will be treated as a **logical AND**. A pipe symbol | will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe (`|`) | will be treated as a **logical OR**. AND has higher precedence than OR. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. From 241627659e2d7f8dd65599aafb6c4b68a4b7e883 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 00:38:39 +0100 Subject: [PATCH 401/638] Fix formatting bug --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index eb5921406..82426449c 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe (`|`) | will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe | will be treated as a **logical OR**. AND has higher precedence than OR. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. From efcdd35cbf9ffe11f064871d302039559a7efa87 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 00:39:21 +0100 Subject: [PATCH 402/638] Damn --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 82426449c..06d2a8c5d 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe | will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe (|) will be treated as a **logical OR**. AND has higher precedence than OR. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. From 391a5390f27dd57041fa006ee1219d99f81c1d19 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 00:51:44 +0100 Subject: [PATCH 403/638] Typo fix, cheers @rdlowrey --- src/Composer/Command/DiagnoseCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 3994138f4..f787f1886 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -183,7 +183,7 @@ EOT try { $this->rfs->getContents('packagist.org', $url, false, array('http' => array('request_fulluri' => false))); } catch (TransportException $e) { - return 'Unable to assert the situation, maybe packagist.org is down ('.$e->getMessage().')'; + return 'Unable to assess the situation, maybe packagist.org is down ('.$e->getMessage().')'; } return 'It seems there is a problem with your proxy server, try setting the "HTTP_PROXY_REQUEST_FULLURI" and "HTTPS_PROXY_REQUEST_FULLURI" environment variables to "false"'; @@ -212,7 +212,7 @@ EOT try { $this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false))); } catch (TransportException $e) { - return 'Unable to assert the situation, maybe github is down ('.$e->getMessage().')'; + return 'Unable to assess the situation, maybe github is down ('.$e->getMessage().')'; } return 'It seems there is a problem with your proxy server, try setting the "HTTPS_PROXY_REQUEST_FULLURI" environment variable to "false"'; From 536a1fdca1002965c6697ef7a5b4c14146816fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20JOURDIN?= Date: Tue, 30 Sep 2014 11:02:20 +0200 Subject: [PATCH 404/638] Update symfony/process to fix hhvm issues --- composer.lock | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/composer.lock b/composer.lock index b3a509716..cfeea277e 100644 --- a/composer.lock +++ b/composer.lock @@ -222,17 +222,17 @@ }, { "name": "symfony/process", - "version": "v2.5.4", + "version": "v2.5.5", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "136cf0bdaacea81f779583376d47dd8aef4fc6ba" + "reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/136cf0bdaacea81f779583376d47dd8aef4fc6ba", - "reference": "136cf0bdaacea81f779583376d47dd8aef4fc6ba", + "url": "https://api.github.com/repos/symfony/Process/zipball/8a1ec96c4e519cee0fb971ea48a1eb7369dda54b", + "reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b", "shasum": "" }, "require": { @@ -265,7 +265,7 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-08-31 03:22:04" + "time": "2014-09-23 05:25:11" } ], "packages-dev": [ @@ -683,18 +683,12 @@ "time": "2014-08-31 03:22:04" } ], - "aliases": [ - - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": [ - - ], + "stability-flags": [], "prefer-stable": false, "platform": { "php": ">=5.3.2" }, - "platform-dev": [ - - ] + "platform-dev": [] } From 472c8a5113876494e19f412e6c5a4ccf112611dd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 15:17:53 +0100 Subject: [PATCH 405/638] Fix tests on windows, fixes #3141 --- tests/Composer/Test/ConfigTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 5afbabffa..705fe78e0 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -115,7 +115,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $config->merge(array('config' => array('a' => 'b', 'c' => '{$a}'))); $config->merge(array('config' => array('bin-dir' => '$HOME', 'cache-dir' => '~/foo/'))); - $home = rtrim(getenv('HOME'), '\\/'); + $home = rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '\\/'); $this->assertEquals('b', $config->get('c')); $this->assertEquals($home.'/', $config->get('bin-dir')); $this->assertEquals($home.'/foo', $config->get('cache-dir')); From efcdb394d3ded5dfd7f358d7d025059b494ee406 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 30 Sep 2014 16:26:55 +0100 Subject: [PATCH 406/638] Rename *-solve-dependencies to *-dependencies-solving --- doc/articles/scripts.md | 2 ++ src/Composer/Installer.php | 8 ++++---- src/Composer/Installer/InstallerEvents.php | 8 ++++---- src/Composer/Script/ScriptEvents.php | 1 - .../EventDispatcher/EventDispatcherTest.php | 17 +++++++++-------- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index adbf07f6b..6dece97e9 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -26,6 +26,8 @@ Composer fires the following named events during its execution process: - **post-update-cmd**: occurs after the `update` command is executed. - **pre-status-cmd**: occurs before the `status` command is executed. - **post-status-cmd**: occurs after the `status` command is executed. +- **pre-dependencies-solving**: occurs before the dependencies are resolved. +- **post-dependencies-solving**: occurs after the dependencies are resolved. - **pre-package-install**: occurs before a package is installed. - **post-package-install**: occurs after a package is installed. - **pre-package-update**: occurs before a package is updated. diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 7c252c5b0..2899e2bdb 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -261,10 +261,10 @@ class Installer $request->install($link->getTarget(), $link->getConstraint()); } - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); $ops = $solver->solve($request); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request, $ops); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $ops); foreach ($ops as $op) { if ($op->getJobType() === 'uninstall') { $devPackages[] = $op->getPackage(); @@ -467,11 +467,11 @@ class Installer $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links'); // solve dependencies - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); try { $operations = $solver->solve($request); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_SOLVE_DEPENDENCIES, $policy, $pool, $installedRepo, $request, $operations); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $operations); } catch (SolverProblemsException $e) { $this->io->write('Your requirements could not be resolved to an installable set of packages.'); $this->io->write($e->getMessage()); diff --git a/src/Composer/Installer/InstallerEvents.php b/src/Composer/Installer/InstallerEvents.php index 5929b0964..e05c92587 100644 --- a/src/Composer/Installer/InstallerEvents.php +++ b/src/Composer/Installer/InstallerEvents.php @@ -20,7 +20,7 @@ namespace Composer\Installer; class InstallerEvents { /** - * The PRE_SOLVE_DEPENDENCIES event occurs as a installer begins + * The PRE_DEPENDENCIES_SOLVING event occurs as a installer begins * resolve operations. * * The event listener method receives a @@ -28,10 +28,10 @@ class InstallerEvents * * @var string */ - const PRE_SOLVE_DEPENDENCIES = 'pre-solve-dependencies'; + const PRE_DEPENDENCIES_SOLVING = 'pre-dependencies-solving'; /** - * The POST_SOLVE_DEPENDENCIES event occurs as a installer after + * The POST_DEPENDENCIES_SOLVING event occurs as a installer after * resolve operations. * * The event listener method receives a @@ -39,5 +39,5 @@ class InstallerEvents * * @var string */ - const POST_SOLVE_DEPENDENCIES = 'post-solve-dependencies'; + const POST_DEPENDENCIES_SOLVING = 'post-dependencies-solving'; } diff --git a/src/Composer/Script/ScriptEvents.php b/src/Composer/Script/ScriptEvents.php index 64ecbc8c5..616b2b97e 100644 --- a/src/Composer/Script/ScriptEvents.php +++ b/src/Composer/Script/ScriptEvents.php @@ -182,5 +182,4 @@ class ScriptEvents * @var string */ const POST_ARCHIVE_CMD = 'post-archive-cmd'; - } diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index a6cad7602..69e1de290 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -14,8 +14,9 @@ namespace Composer\Test\EventDispatcher; use Composer\EventDispatcher\Event; use Composer\EventDispatcher\EventDispatcher; +use Composer\Installer\InstallerEvents; use Composer\TestCase; -use Composer\Script; +use Composer\Script\ScriptEvents; use Composer\Util\ProcessExecutor; class EventDispatcherTest extends TestCase @@ -34,7 +35,7 @@ class EventDispatcherTest extends TestCase ->method('write') ->with('Script Composer\Test\EventDispatcher\EventDispatcherTest::call handling the post-install-cmd event terminated with an exception'); - $dispatcher->dispatchCommandEvent("post-install-cmd", false); + $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); } /** @@ -63,7 +64,7 @@ class EventDispatcherTest extends TestCase ->with($command) ->will($this->returnValue(0)); - $dispatcher->dispatchCommandEvent("post-install-cmd", false); + $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); } public function testDispatcherCanExecuteCliAndPhpInSameEventScriptStack() @@ -99,7 +100,7 @@ class EventDispatcherTest extends TestCase ->with('Composer\Test\EventDispatcher\EventDispatcherTest', 'someMethod') ->will($this->returnValue(true)); - $dispatcher->dispatchCommandEvent("post-install-cmd", false); + $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); } private function getDispatcherStubForListenersTest($listeners, $io) @@ -145,7 +146,7 @@ class EventDispatcherTest extends TestCase ->will($this->returnValue($listener)); ob_start(); - $dispatcher->dispatchCommandEvent("post-install-cmd", false); + $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); $this->assertEquals('foo', trim(ob_get_clean())); } @@ -171,7 +172,7 @@ class EventDispatcherTest extends TestCase ->with($this->equalTo('Script '.$code.' handling the post-install-cmd event returned with an error')); $this->setExpectedException('RuntimeException'); - $dispatcher->dispatchCommandEvent("post-install-cmd", false); + $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); } public function testDispatcherInstallerEvents() @@ -195,8 +196,8 @@ class EventDispatcherTest extends TestCase $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock(); $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock(); - $dispatcher->dispatchInstallerEvent("pre-solve-dependencies", $policy, $pool, $installedRepo, $request); - $dispatcher->dispatchInstallerEvent("post-solve-dependencies", $policy, $pool, $installedRepo, $request, array()); + $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); + $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, array()); } public static function call() From c698aa3a2b3152eaebc89bbd3054d1bcfedce27f Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 2 Oct 2014 00:17:20 -0300 Subject: [PATCH 407/638] Fix missing validation on default value for author input The default author value on the composer init command is not validated against `parseAuthorString` method and thus not being re-prompted, finally throwing an InvalidArgumentException when it tries to generate the composer.json file. The changes forces the validation of both the entered author string or the default value. --- src/Composer/Command/InitCommand.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 8aa734630..f92615f7b 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -228,10 +228,7 @@ EOT $output, $dialog->getQuestion('Author', $author), function ($value) use ($self, $author) { - if (null === $value) { - return $author; - } - + $value = $value ?: $author; $author = $self->parseAuthorString($value); return sprintf('%s <%s>', $author['name'], $author['email']); From 2f66d0c703c728248d872ad11b3b797b96d44a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Thu, 2 Oct 2014 10:47:34 +0200 Subject: [PATCH 408/638] Fix phpdoc --- src/Composer/Package/CompletePackage.php | 2 +- src/Composer/Package/PackageInterface.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index 418c87d9e..a884174af 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -47,7 +47,7 @@ class CompletePackage extends Package implements CompletePackageInterface /** * Set the repositories * - * @param string $repositories + * @param array $repositories */ public function setRepositories($repositories) { diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 50553f9c4..a51274d5b 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -118,7 +118,7 @@ interface PackageInterface /** * Returns the repository urls of this package including mirrors, e.g. git://github.com/naderman/composer.git * - * @return string + * @return array */ public function getSourceUrls(); @@ -153,7 +153,7 @@ interface PackageInterface /** * Returns the urls of the distribution archive of this version, including mirrors * - * @return string + * @return array */ public function getDistUrls(); From 432cdbcb313669ffe9c0b10da4f79cead0a0e44d Mon Sep 17 00:00:00 2001 From: Aydin Date: Thu, 2 Oct 2014 12:07:31 +0100 Subject: [PATCH 409/638] Allow to select virtual packages --- src/Composer/Command/InitCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 8aa734630..7dd9322e2 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -333,8 +333,7 @@ EOT } while (null !== $package = $dialog->ask($output, $prompt)) { - $matches = $this->findPackages($package); - + $matches = array_values($this->findPackages($package)); if (count($matches)) { $output->writeln(array( '', From 1e4229e22aefe50582734964d027fc1bfec16b1d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 2 Oct 2014 12:33:56 +0100 Subject: [PATCH 410/638] Adjust APC check, refs #264 --- src/Composer/Command/DiagnoseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index f787f1886..ced252787 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -312,7 +312,7 @@ EOT $warnings['openssl'] = true; } - if (!defined('HHVM_VERSION') && ini_get('apc.enable_cli')) { + if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) { $warnings['apc_cli'] = true; } From d6d087d3487ea99046ef30c299bc46c8fe8e0874 Mon Sep 17 00:00:00 2001 From: Rafael Dohms Date: Thu, 2 Oct 2014 23:04:35 +0200 Subject: [PATCH 411/638] Support for Abandoned in Packages Added parsing for abandoned property into the CompletePackage Object. --- src/Composer/Package/AliasPackage.php | 8 +++++ src/Composer/Package/CompletePackage.php | 29 +++++++++++++++++++ .../Package/CompletePackageInterface.php | 14 +++++++++ src/Composer/Package/Dumper/ArrayDumper.php | 4 ++- src/Composer/Package/Loader/ArrayLoader.php | 4 +++ .../Test/Package/Dumper/ArrayDumperTest.php | 22 +++++++++++++- .../Test/Package/Loader/ArrayLoaderTest.php | 27 ++++++++++++++++- 7 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 183f2c740..3ab9c944d 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -333,6 +333,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getArchiveExcludes(); } + public function isAbandoned() + { + return $this->aliasOf->isAbandoned(); + } + public function getReplacementPackage() + { + return $this->aliasOf->getReplacementPackage(); + } public function __toString() { return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')'; diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index a884174af..af2e2bc42 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -27,6 +27,7 @@ class CompletePackage extends Package implements CompletePackageInterface protected $homepage; protected $scripts = array(); protected $support = array(); + protected $abandoned = false; /** * @param array $scripts @@ -169,4 +170,32 @@ class CompletePackage extends Package implements CompletePackageInterface { return $this->support; } + + /** + * @return boolean + */ + public function isAbandoned() + { + return (boolean) $this->abandoned; + } + + /** + * @param boolean|string $abandoned + */ + public function setAbandoned($abandoned) + { + $this->abandoned = $abandoned; + } + + /** + * If the package is abandoned and has a suggested replacement, this method returns it + * + * @return string|null + */ + public function getReplacementPackage() + { + return $this->abandoned ?: null; + } + + } diff --git a/src/Composer/Package/CompletePackageInterface.php b/src/Composer/Package/CompletePackageInterface.php index a341766d3..8263a6535 100644 --- a/src/Composer/Package/CompletePackageInterface.php +++ b/src/Composer/Package/CompletePackageInterface.php @@ -78,4 +78,18 @@ interface CompletePackageInterface extends PackageInterface * @return array */ public function getSupport(); + + /** + * Returns if the package is abandoned or not + * + * @return boolean + */ + public function isAbandoned(); + + /** + * If the package is abandoned and has a suggested replacement, this method returns it + * + * @return string + */ + public function getReplacementPackage(); } diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 67318c04a..50b2eb610 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -97,7 +97,7 @@ class ArrayDumper 'homepage', 'keywords', 'repositories', - 'support', + 'support' ); $data = $this->dumpValues($package, $keys, $data); @@ -105,6 +105,8 @@ class ArrayDumper if (isset($data['keywords']) && is_array($data['keywords'])) { sort($data['keywords']); } + + $data['abandoned'] = $package->getReplacementPackage() ?: false; } if ($package instanceof RootPackageInterface) { diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 142b15c72..e548c87e7 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -195,6 +195,10 @@ class ArrayLoader implements LoaderInterface if (isset($config['support'])) { $package->setSupport($config['support']); } + + if (isset($config['abandoned'])) { + $package->setAbandoned($config['abandoned']); + } } if ($aliasNormalized = $this->getBranchAlias($config)) { diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 5576e3d05..656ec96bc 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -45,7 +45,8 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase array( 'name' => 'foo', 'version' => '1.0', - 'version_normalized' => '1.0.0.0' + 'version_normalized' => '1.0.0.0', + 'abandoned' => false ), $config ); @@ -62,12 +63,31 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase $this->assertSame('dev', $config['minimum-stability']); } + public function testDumpAbandoned() + { + $this->packageExpects('getReplacementPackage', true); + + $config = $this->dumper->dump($this->package); + + $this->assertSame(true, $config['abandoned']); + } + + public function testDumpAbandonedReplacement() + { + $this->packageExpects('getReplacementPackage', 'foo/bar'); + + $config = $this->dumper->dump($this->package); + + $this->assertSame('foo/bar', $config['abandoned']); + } + /** * @dataProvider getKeys */ public function testKeys($key, $value, $method = null, $expectedValue = null) { $this->packageExpects('get'.ucfirst($method ?: $key), $value); + $this->packageExpects('isAbandoned', $value); $config = $this->dumper->dump($this->package); diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 04a537abd..6e4e2f5ee 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -117,7 +117,8 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase 'archive' => array( 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), ), - 'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) + 'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')), + 'abandoned' => 'foo/bar' ); $package = $this->loader->load($config); @@ -138,4 +139,28 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); } + + public function testAbandoned() + { + $config = array( + 'name' => 'A', + 'version' => '1.2.3.4', + 'abandoned' => 'foo/bar' + ); + + $package = $this->loader->load($config); + $this->assertTrue($package->isAbandoned()); + $this->assertEquals('foo/bar', $package->getReplacementPackage()); + } + + public function testNotAbandoned() + { + $config = array( + 'name' => 'A', + 'version' => '1.2.3.4' + ); + + $package = $this->loader->load($config); + $this->assertFalse($package->isAbandoned()); + } } From 5ed01fb60d6b9056630ead23dcd96327c332ad86 Mon Sep 17 00:00:00 2001 From: Rafael Dohms Date: Thu, 2 Oct 2014 23:19:27 +0200 Subject: [PATCH 412/638] Abandoned support in `show` Executing `composer show` on an abandoned package displays the warnings for package status. --- src/Composer/Command/ShowCommand.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index dbf41ceb9..28ea81028 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -294,6 +294,16 @@ EOT $output->writeln('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); $output->writeln('names : ' . implode(', ', $package->getNames())); + if ($package->isAbandoned()) { + $replacement = ($package->getReplacementPackage() !== null) + ? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.' + : null; + + $output->writeln( + sprintf('Attention: This package is abandoned and no longer maintained.%s', $replacement) + ); + } + if ($package->getSupport()) { $output->writeln("\nsupport"); foreach ($package->getSupport() as $type => $value) { From f9f2bbac59ed1de898ccf2533e3e290bd80afc26 Mon Sep 17 00:00:00 2001 From: Rafael Dohms Date: Fri, 3 Oct 2014 14:48:28 +0200 Subject: [PATCH 413/638] Abandoned packages support Added warning to the installer process. If any installed packages are flagged, the user will get a warning at the end of the install/update process. --- src/Composer/Installer.php | 20 +++++++++++ .../Fixtures/installer/abandoned-listed.test | 36 +++++++++++++++++++ .../installer/install-dev-using-dist.test | 3 +- ...e-downgrades-non-whitelisted-unstable.test | 6 ++-- .../installer/partial-update-from-lock.test | 6 ++-- .../partial-update-without-lock.test | 8 ++--- .../Fixtures/installer/update-alias-lock.test | 5 +-- ...pdating-dev-updates-url-and-reference.test | 3 +- tests/Composer/Test/InstallerTest.php | 2 +- 9 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/abandoned-listed.test diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2899e2bdb..00276f7e5 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -31,6 +31,7 @@ use Composer\Installer\NoopInstaller; use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Package\AliasPackage; +use Composer\Package\CompletePackage; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Locker; @@ -239,6 +240,25 @@ class Installer } } + # Find abandoned packages and warn user + foreach ($localRepo->getPackages() as $package) { + if (!$package instanceof CompletePackage || !$package->isAbandoned()) { + continue; + } + + $replacement = (is_string($package->getReplacementPackage())) + ? 'Use ' . $package->getReplacementPackage() . ' instead' + : 'No replacement was suggested'; + + $this->io->write( + sprintf( + "Package %s is abandoned, you should avoid using it. %s.", + $package->getPrettyName(), + $replacement + ) + ); + } + if (!$this->dryRun) { // write lock if ($this->update || !$this->locker->isLocked()) { diff --git a/tests/Composer/Test/Fixtures/installer/abandoned-listed.test b/tests/Composer/Test/Fixtures/installer/abandoned-listed.test new file mode 100644 index 000000000..18f47732e --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/abandoned-listed.test @@ -0,0 +1,36 @@ +--TEST-- +Abandoned packages are flagged +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "abandoned": true } + ] + }, + { + "type": "package", + "package": [ + { "name": "c/c", "version": "1.0.0", "abandoned": "b/b" } + ] + } + ], + "require": { + "a/a": "1.0.0", + "c/c": "1.0.0" + } +} +--RUN-- +install +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Installing dependencies (including require-dev) +Package a/a is abandoned, you should avoid using it. No replacement was suggested. +Package c/c is abandoned, you should avoid using it. Use b/b instead. +Writing lock file +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) +Installing c/c (1.0.0) 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 af4eed811..5495b60f3 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -38,7 +38,8 @@ install --prefer-dist "reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6", "shasum": null }, - "type": "library" + "type": "library", + "abandoned": false } ], "packages-dev": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test index f9fd5058a..dd085c5ac 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test @@ -50,9 +50,9 @@ update c/uptodate --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library" }, - { "name": "b/unstable", "version": "1.0.0", "type": "library" }, - { "name": "c/uptodate", "version": "2.0.0", "type": "library" } + { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "c/uptodate", "version": "2.0.0", "type": "library", "abandoned": false } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test index 5b904f9b5..b63694a1e 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test @@ -50,9 +50,9 @@ update b/unstable --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library" }, - { "name": "b/unstable", "version": "1.0.0", "type": "library" }, - { "name": "c/uptodate", "version": "1.0.0", "type": "library" } + { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "c/uptodate", "version": "1.0.0", "type": "library", "abandoned": false } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test index 224e58f7d..9e4ff30e3 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test @@ -33,10 +33,10 @@ update b/unstable --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library" }, - { "name": "b/unstable", "version": "1.0.0", "type": "library" }, - { "name": "c/uptodate", "version": "1.0.0", "type": "library" }, - { "name": "d/removed", "version": "1.0.0", "type": "library" } + { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "c/uptodate", "version": "1.0.0", "type": "library", "abandoned": false }, + { "name": "d/removed", "version": "1.0.0", "type": "library", "abandoned": false } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test index 0fc5fe301..256ce7e47 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test @@ -58,7 +58,8 @@ update "name": "a/a", "version": "dev-master", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "source": { "reference": "master", "type": "git", "url": "" }, - "type": "library" + "type": "library", + "abandoned": false } ], "packages-dev": [], @@ -70,4 +71,4 @@ update "platform-dev": [] } --EXPECT-- -Updating a/a (dev-master 1234) to a/a (dev-master master) \ No newline at end of file +Updating a/a (dev-master 1234) to a/a (dev-master master) diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test index 849296850..aedc79fee 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test @@ -51,7 +51,8 @@ update "name": "a/a", "version": "dev-master", "type": "library", "source": { "reference": "newref", "url": "newurl", "type": "git" }, - "dist": { "reference": "newref", "url": "newurl", "type": "zip", "shasum": "" } + "dist": { "reference": "newref", "url": "newurl", "type": "zip", "shasum": "" }, + "abandoned": false } ], "packages-dev": [], diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index a88ce7c21..5566e7c55 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -304,7 +304,7 @@ class InstallerTest extends TestCase die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); } - $tests[] = 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, $expectExitCode); } return $tests; From 346133d2a112dbc52163eceeee67814d351efe3f Mon Sep 17 00:00:00 2001 From: Rafael Dohms Date: Fri, 3 Oct 2014 15:12:16 +0200 Subject: [PATCH 414/638] PR Review - reverted comma removal - made getReplacementPackage consistent - removed abandoned flag if false --- src/Composer/Package/CompletePackage.php | 4 +--- src/Composer/Package/Dumper/ArrayDumper.php | 6 ++++-- .../Test/Fixtures/installer/install-dev-using-dist.test | 3 +-- ...artial-update-downgrades-non-whitelisted-unstable.test | 6 +++--- .../Test/Fixtures/installer/partial-update-from-lock.test | 6 +++--- .../Fixtures/installer/partial-update-without-lock.test | 8 ++++---- .../Test/Fixtures/installer/update-alias-lock.test | 3 +-- .../installer/updating-dev-updates-url-and-reference.test | 3 +-- tests/Composer/Test/Package/Dumper/ArrayDumperTest.php | 5 +++-- 9 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index af2e2bc42..27c9abeca 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -194,8 +194,6 @@ class CompletePackage extends Package implements CompletePackageInterface */ public function getReplacementPackage() { - return $this->abandoned ?: null; + return is_string($this->abandoned)? $this->abandoned : null; } - - } diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 50b2eb610..714c5183b 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -97,7 +97,7 @@ class ArrayDumper 'homepage', 'keywords', 'repositories', - 'support' + 'support', ); $data = $this->dumpValues($package, $keys, $data); @@ -106,7 +106,9 @@ class ArrayDumper sort($data['keywords']); } - $data['abandoned'] = $package->getReplacementPackage() ?: false; + if ($package->isAbandoned()) { + $data['abandoned'] = $package->getReplacementPackage() ?: true; + } } if ($package instanceof RootPackageInterface) { 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 5495b60f3..af4eed811 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -38,8 +38,7 @@ install --prefer-dist "reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6", "shasum": null }, - "type": "library", - "abandoned": false + "type": "library" } ], "packages-dev": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test index dd085c5ac..f9fd5058a 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test @@ -50,9 +50,9 @@ update c/uptodate --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "c/uptodate", "version": "2.0.0", "type": "library", "abandoned": false } + { "name": "a/old", "version": "1.0.0", "type": "library" }, + { "name": "b/unstable", "version": "1.0.0", "type": "library" }, + { "name": "c/uptodate", "version": "2.0.0", "type": "library" } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test index b63694a1e..5b904f9b5 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test @@ -50,9 +50,9 @@ update b/unstable --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "c/uptodate", "version": "1.0.0", "type": "library", "abandoned": false } + { "name": "a/old", "version": "1.0.0", "type": "library" }, + { "name": "b/unstable", "version": "1.0.0", "type": "library" }, + { "name": "c/uptodate", "version": "1.0.0", "type": "library" } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test index 9e4ff30e3..224e58f7d 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test @@ -33,10 +33,10 @@ update b/unstable --EXPECT-LOCK-- { "packages": [ - { "name": "a/old", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "b/unstable", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "c/uptodate", "version": "1.0.0", "type": "library", "abandoned": false }, - { "name": "d/removed", "version": "1.0.0", "type": "library", "abandoned": false } + { "name": "a/old", "version": "1.0.0", "type": "library" }, + { "name": "b/unstable", "version": "1.0.0", "type": "library" }, + { "name": "c/uptodate", "version": "1.0.0", "type": "library" }, + { "name": "d/removed", "version": "1.0.0", "type": "library" } ], "packages-dev": [], "aliases": [], diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test index 256ce7e47..bfe0bb8a8 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test @@ -58,8 +58,7 @@ update "name": "a/a", "version": "dev-master", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "source": { "reference": "master", "type": "git", "url": "" }, - "type": "library", - "abandoned": false + "type": "library" } ], "packages-dev": [], diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test index aedc79fee..849296850 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test @@ -51,8 +51,7 @@ update "name": "a/a", "version": "dev-master", "type": "library", "source": { "reference": "newref", "url": "newurl", "type": "git" }, - "dist": { "reference": "newref", "url": "newurl", "type": "zip", "shasum": "" }, - "abandoned": false + "dist": { "reference": "newref", "url": "newurl", "type": "zip", "shasum": "" } } ], "packages-dev": [], diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 656ec96bc..f1889a1ce 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -45,8 +45,7 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase array( 'name' => 'foo', 'version' => '1.0', - 'version_normalized' => '1.0.0.0', - 'abandoned' => false + 'version_normalized' => '1.0.0.0' ), $config ); @@ -65,6 +64,7 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase public function testDumpAbandoned() { + $this->packageExpects('isAbandoned', true); $this->packageExpects('getReplacementPackage', true); $config = $this->dumper->dump($this->package); @@ -74,6 +74,7 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase public function testDumpAbandonedReplacement() { + $this->packageExpects('isAbandoned', true); $this->packageExpects('getReplacementPackage', 'foo/bar'); $config = $this->dumper->dump($this->package); From ad1d0be4208fcae987e8ad7e33fdeba9a2df8d65 Mon Sep 17 00:00:00 2001 From: Artur Eshenbrener Date: Fri, 3 Oct 2014 18:28:16 +0400 Subject: [PATCH 415/638] Accessing to option 'optimize-autoloder' inside 'post-autoload-dump' script event. --- src/Composer/Autoload/AutoloadGenerator.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 836acafe8..dd23cee8a 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -53,7 +53,9 @@ class AutoloadGenerator public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') { - $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode); + $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array( + 'optimize' => (bool) $scanPsr0Packages + )); $filesystem = new Filesystem(); $filesystem->ensureDirectoryExists($config->get('vendor-dir')); @@ -235,7 +237,9 @@ EOF; fclose($targetLoader); unset($sourceLoader, $targetLoader); - $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode); + $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, [ + 'optimize' => (bool) $scanPsr0Packages, + ]); } public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) From 56482cff0aeb3e40de6c561d927c3bcec9d3cacd Mon Sep 17 00:00:00 2001 From: Artur Eshenbrener Date: Fri, 3 Oct 2014 18:31:02 +0400 Subject: [PATCH 416/638] Changed short array syntzx to long --- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index dd23cee8a..1d9f5b9c0 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -237,9 +237,9 @@ EOF; fclose($targetLoader); unset($sourceLoader, $targetLoader); - $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, [ + $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array( 'optimize' => (bool) $scanPsr0Packages, - ]); + )); } public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) From ed54e8346d8bfa4da6a49875c50cbcbc01303404 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 4 Oct 2014 12:33:28 +0100 Subject: [PATCH 417/638] Fix json test, fixes #3326 --- tests/Composer/Test/Json/JsonFileTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 6e0c46f12..6566e4cc4 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -131,7 +131,7 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testFormatEmptyArray() { $data = array('test' => array(), 'test2' => new \stdClass); - if (PHP_VERSION_ID < 50429 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50513)) { + if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) { $json = '{ "test": [ From fc14d3349114b535006217099e021a84b7345dea Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 4 Oct 2014 16:34:26 +0100 Subject: [PATCH 418/638] Fix json nesting issue, fixes #3323 --- src/Composer/Json/JsonManipulator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index b64ba7a95..6dccc62dc 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -30,7 +30,7 @@ class JsonManipulator { if (!self::$RECURSE_BLOCKS) { self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*'; - self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\])*'; + self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*'; self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])+"'; self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})'; } From 109f4ffd5e931382455170883c6724f21cfcfd4e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 4 Oct 2014 17:00:12 +0100 Subject: [PATCH 419/638] Normalize json across all php versions, fixes #3226 --- src/Composer/Json/JsonFile.php | 10 +++++++++- src/Composer/Json/JsonFormatter.php | 2 +- tests/Composer/Test/Json/JsonFileTest.php | 13 +------------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 2d35b9671..80718348c 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -184,7 +184,15 @@ class JsonFile public static function encode($data, $options = 448) { if (version_compare(PHP_VERSION, '5.4', '>=')) { - return json_encode($data, $options); + $json = json_encode($data, $options); + + // compact brackets to follow recent php versions + if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) { + $json = preg_replace('/\[\s+\]/', '[]', $json); + $json = preg_replace('/\{\s+\}/', '{}', $json); + } + + return $json; } $json = json_encode($data); diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php index 614683608..025a53950 100644 --- a/src/Composer/Json/JsonFormatter.php +++ b/src/Composer/Json/JsonFormatter.php @@ -102,7 +102,7 @@ class JsonFormatter } } else { // Collapse empty {} and [] - $result = rtrim($result)."\n\n".$indentStr; + $result = rtrim($result); } } diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 6566e4cc4..cf89da35e 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -131,21 +131,10 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testFormatEmptyArray() { $data = array('test' => array(), 'test2' => new \stdClass); - if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) { - $json = '{ - "test": [ - - ], - "test2": { - - } -}'; - } else { - $json = '{ + $json = '{ "test": [], "test2": {} }'; - } $this->assertJsonFormat($json, $data); } From 2e1373b339def68273267d7cbeb3292626005f3a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 4 Oct 2014 17:23:04 +0100 Subject: [PATCH 420/638] Update require message and delete empty file at the end in case of failure, fixes #3260 --- doc/03-cli.md | 2 +- src/Composer/Command/RequireCommand.php | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7145132c8..2dd0a6c36 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -139,7 +139,7 @@ php composer.phar update vendor/* ## require The `require` command adds new packages to the `composer.json` file from -the current directory. +the current directory. If no file exists one will be created on the fly. ```sh php composer.phar require diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 9b4b50df1..682a44e66 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -59,6 +59,7 @@ EOT { $file = Factory::getComposerFile(); + $newlyCreated = !file_exists($file); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $output->writeln(''.$file.' could not be created.'); @@ -105,7 +106,7 @@ EOT $json->write($composer); } - $output->writeln(''.$file.' has been updated'); + $output->writeln(''.$file.' has been '.($newlyCreated ? 'created' : 'updated').''); if ($input->getOption('no-update')) { return 0; @@ -134,8 +135,13 @@ EOT $status = $install->run(); if ($status !== 0) { - $output->writeln("\n".'Installation failed, reverting '.$file.' to its original content.'); - file_put_contents($json->getPath(), $composerBackup); + if ($newlyCreated) { + $output->writeln("\n".'Installation failed, deleting '.$file.'.'); + unlink($json->getPath()); + } else { + $output->writeln("\n".'Installation failed, reverting '.$file.' to its original content.'); + file_put_contents($json->getPath(), $composerBackup); + } } return $status; From 2cdc647f9fcd8523a1b36058ded924aacecbd3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= Date: Sat, 4 Oct 2014 20:48:31 +0200 Subject: [PATCH 421/638] Readme: update coding standard link Maybe it's on purpose, but current link leads to documentation, where deprecated note. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a954bcfc2..cf07c54b5 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ merged. This is to ensure proper review of all the code. Fork the project, create a feature branch, and send us a pull request. To ensure a consistent code base, you should make sure the code follows -the [Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html) +the [Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html) which we borrowed from Symfony. If you would like to help take a look at the [list of issues](http://github.com/composer/composer/issues). From 4685b4bf495a5aef8e5b5384f52153f92b6994fc Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sun, 5 Oct 2014 14:30:44 +0200 Subject: [PATCH 422/638] Fixed the resetting of the json file on removal failures Closes #3329 --- src/Composer/Command/RemoveCommand.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index be110d650..ee1754c65 100755 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -59,11 +59,11 @@ EOT $file = Factory::getComposerFile(); - $json = new JsonFile($file); - $composer = $json->read(); - $composerBackup = file_get_contents($json->getPath()); + $jsonFile = new JsonFile($file); + $composer = $jsonFile->read(); + $composerBackup = file_get_contents($jsonFile->getPath()); - $json = new JsonConfigSource($json); + $json = new JsonConfigSource($jsonFile); $type = $input->getOption('dev') ? 'require-dev' : 'require'; $altType = !$input->getOption('dev') ? 'require-dev' : 'require'; @@ -110,7 +110,7 @@ EOT $status = $install->run(); if ($status !== 0) { $output->writeln("\n".'Removal failed, reverting '.$file.' to its original content.'); - file_put_contents($json->getPath(), $composerBackup); + file_put_contents($jsonFile->getPath(), $composerBackup); } return $status; From 95d49ce638ba0884a9f57de609184c9cca6e778c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 6 Oct 2014 12:41:54 +0100 Subject: [PATCH 423/638] Add additional test for beta without dash separator support, fixes #3331 --- tests/Composer/Test/Package/Version/VersionParserTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index f54371d65..25c2b9898 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -89,6 +89,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'forces w.x.y.z' => array('1.0-dev', '1.0.0.0-dev'), 'forces w.x.y.z/2' => array('0', '0.0.0.0'), 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), + 'parses long/2' => array('10.4.13beta2', '10.4.13.0-beta2'), 'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'), 'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'), 'strips leading v' => array('v1.0.0', '1.0.0.0'), From 26122cff430ea60cbafcef9ef2d07319b4ad543a Mon Sep 17 00:00:00 2001 From: Thomas Punt Date: Sun, 5 Oct 2014 22:57:01 +0100 Subject: [PATCH 424/638] Updated example to have a valid tag name. --- doc/02-libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 81ec3edaa..561f3aa79 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -85,7 +85,7 @@ Here are a few examples of valid tag names: - 1.0.0 - v1.0.0 - 1.10.5-RC1 -- v4.4.4beta2 +- v4.4.4-beta2 - v2.0.0-alpha - v2.0.4-p1 From 382b6c64d514922dda572624cb3e27553dbdc434 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Oct 2014 10:17:23 +0100 Subject: [PATCH 425/638] Output CWD change, refs #3332 --- src/Composer/Console/Application.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 266767d9b..7148a9a11 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -109,6 +109,9 @@ class Application extends BaseApplication if ($newWorkDir = $this->getNewWorkingDir($input)) { $oldWorkingDir = getcwd(); chdir($newWorkDir); + if ($output->getVerbosity() >= 4) { + $output->writeln('Changed CWD to ' . getcwd()); + } } // add non-standard scripts as own commands From 4ce925225585e0b57cfa4bf8770b02db346f24b1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Oct 2014 11:05:05 +0100 Subject: [PATCH 426/638] Avoid passing args to CLI handlers when none are needed, fixes #3332 --- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- src/Composer/EventDispatcher/Event.php | 23 ++++++++++++++++--- .../EventDispatcher/EventDispatcher.php | 14 ++++++----- src/Composer/Plugin/CommandEvent.php | 7 +++--- src/Composer/Script/Event.php | 7 +++--- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 1d9f5b9c0..45c105ef9 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -53,7 +53,7 @@ class AutoloadGenerator public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') { - $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array( + $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array( 'optimize' => (bool) $scanPsr0Packages )); @@ -237,7 +237,7 @@ EOF; fclose($targetLoader); unset($sourceLoader, $targetLoader); - $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array( + $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array( 'optimize' => (bool) $scanPsr0Packages, )); } diff --git a/src/Composer/EventDispatcher/Event.php b/src/Composer/EventDispatcher/Event.php index 7aff24f11..b9ebeb029 100644 --- a/src/Composer/EventDispatcher/Event.php +++ b/src/Composer/EventDispatcher/Event.php @@ -25,10 +25,15 @@ class Event protected $name; /** - * @var array Arguments passed by the user + * @var array Arguments passed by the user, these will be forwarded to CLI script handlers */ protected $args; + /** + * @var array Flags usable in PHP script handlers + */ + protected $flags; + /** * @var boolean Whether the event should not be passed to more listeners */ @@ -38,12 +43,14 @@ class Event * Constructor. * * @param string $name The event name - * @param array $events Arguments passed by the user + * @param array $args Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument */ - public function __construct($name, array $args = array()) + public function __construct($name, array $args = array(), array $flags = array()) { $this->name = $name; $this->args = $args; + $this->flags = $flags; } /** @@ -66,6 +73,16 @@ class Event return $this->args; } + /** + * Returns the event's flags. + * + * @return array The event flags + */ + public function getFlags() + { + return $this->flags; + } + /** * Checks if stopPropagation has been called * diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 0c62075e9..3026d3c9c 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -81,13 +81,14 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param bool $devMode - * @param array $additionalArgs + * @param array $additionalArgs Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchScript($eventName, $devMode = false, $additionalArgs = array()) + public function dispatchScript($eventName, $devMode = false, $additionalArgs = array(), $flags = array()) { - return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); + return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags)); } /** @@ -109,13 +110,14 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param boolean $devMode Whether or not we are in dev mode - * @param array $additionalArgs + * @param array $additionalArgs Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array()) + public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array(), $flags = array()) { - return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); + return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags)); } diff --git a/src/Composer/Plugin/CommandEvent.php b/src/Composer/Plugin/CommandEvent.php index 1c83db2b9..0697df97a 100644 --- a/src/Composer/Plugin/CommandEvent.php +++ b/src/Composer/Plugin/CommandEvent.php @@ -45,11 +45,12 @@ class CommandEvent extends Event * @param string $commandName The command name * @param InputInterface $input * @param OutputInterface $output - * @param array $events Arguments passed by the user + * @param array $args Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument */ - public function __construct($name, $commandName, $input, $output, array $args = array()) + public function __construct($name, $commandName, $input, $output, array $args = array(), array $flags = array()) { - parent::__construct($name, $args); + parent::__construct($name, $args, $flags); $this->commandName = $commandName; $this->input = $input; $this->output = $output; diff --git a/src/Composer/Script/Event.php b/src/Composer/Script/Event.php index 58fb4788b..bde7e3b6f 100644 --- a/src/Composer/Script/Event.php +++ b/src/Composer/Script/Event.php @@ -46,11 +46,12 @@ class Event extends BaseEvent * @param Composer $composer The composer object * @param IOInterface $io The IOInterface object * @param boolean $devMode Whether or not we are in dev mode - * @param array $events Arguments passed by the user + * @param array $args Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument */ - public function __construct($name, Composer $composer, IOInterface $io, $devMode = false, array $args = array()) + public function __construct($name, Composer $composer, IOInterface $io, $devMode = false, array $args = array(), array $flags = array()) { - parent::__construct($name, $args); + parent::__construct($name, $args, $flags); $this->composer = $composer; $this->io = $io; $this->devMode = $devMode; From 7ccda2b1c3a1bf343ffa3ec00dcf528372477fea Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Oct 2014 14:35:09 +0100 Subject: [PATCH 427/638] Update recommendations, refs #3095 --- doc/articles/troubleshooting.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index 1db90b8d4..922526a52 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -21,7 +21,7 @@ This is a list of common pitfalls on using Composer, and how to avoid them. possible interferences with existing vendor installations or `composer.lock` entries. -5. Try clearing Composer's cache - `rm -r ~/.composer/cache`. +5. Try clearing Composer's cache by running `composer clear-cache`. ## Package not found @@ -41,8 +41,7 @@ This is a list of common pitfalls on using Composer, and how to avoid them. `replace`. 5. If you are updating to a recently published version of a package, be aware that - Packagist has a delay of up to 10 minutes before new packages are added to it's - index. + Packagist has a delay of up to 1 minute before new packages are visible to Composer. ## Package not found on travis-ci.org From df7c8915fa84e65d949daab87e13fc61e705d820 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Oct 2014 15:45:25 +0100 Subject: [PATCH 428/638] Skip notification about matches found in case we have an exact match, refs #3261 --- src/Composer/Command/InitCommand.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 9dcb88d03..84bda9d90 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -336,12 +336,6 @@ EOT $matches = $this->findPackages($package); if (count($matches)) { - $output->writeln(array( - '', - sprintf('Found %s packages matching %s', count($matches), $package), - '' - )); - $exactMatch = null; $choices = array(); foreach ($matches as $position => $foundPackage) { @@ -354,6 +348,12 @@ EOT // no match, prompt which to pick if (!$exactMatch) { + $output->writeln(array( + '', + sprintf('Found %s packages matching %s', count($matches), $package), + '' + )); + $output->writeln($choices); $output->writeln(''); From 73e9db5d9952d52a46ecbc20a269a8c5f9c5b011 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Oct 2014 16:03:19 +0100 Subject: [PATCH 429/638] Fix the problem at the source, refs #3322 --- src/Composer/Command/InitCommand.php | 3 ++- src/Composer/Repository/ArrayRepository.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 59ef2d379..84bda9d90 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -333,7 +333,8 @@ EOT } while (null !== $package = $dialog->ask($output, $prompt)) { - $matches = array_values($this->findPackages($package)); + $matches = $this->findPackages($package); + if (count($matches)) { $exactMatch = null; $choices = array(); diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 9b60b8db8..f08f9cda7 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -98,7 +98,7 @@ class ArrayRepository implements RepositoryInterface } } - return $matches; + return array_values($matches); } /** From b7fa302b901fdcf07b488b5be83f688d013562dd Mon Sep 17 00:00:00 2001 From: alu Date: Thu, 2 Oct 2014 14:01:15 +0900 Subject: [PATCH 430/638] add ignore-platform-package-requirements for update/install commands. --- src/Composer/Command/InstallCommand.php | 2 ++ src/Composer/Command/UpdateCommand.php | 4 +++- src/Composer/DependencyResolver/Solver.php | 19 ++++++++++++--- src/Composer/Installer.php | 17 +++++++++++-- ...-ignore-platform-package-requirements.test | 20 ++++++++++++++++ ...-ignore-platform-package-requirements.test | 24 +++++++++++++++++++ tests/Composer/Test/InstallerTest.php | 6 +++-- 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 4f40837f1..b65e08f0f 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -45,6 +45,7 @@ class InstallCommand extends Command new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), + new InputOption('ignore-platform-package-requirements', null, InputOption::VALUE_NONE, 'Ignore PHP Extention requirements.'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp(<<setDevMode(!$input->getOption('no-dev')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) + ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index d45e386ed..131390dd7 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -45,7 +45,8 @@ class UpdateCommand extends Command new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), - new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.') + new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), + new InputOption('ignore-platform-package-requirements', null, InputOption::VALUE_NONE, 'Ignore PHP Extention requirements.'), )) ->setHelp(<<update
command reads the composer.json file from the @@ -119,6 +120,7 @@ EOT ->setUpdate(true) ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) + ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 6d6088729..c6ad2b17a 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -13,6 +13,7 @@ namespace Composer\DependencyResolver; use Composer\Repository\RepositoryInterface; +use Composer\Repository\PlatformRepository; /** * @author Nils Adermann @@ -129,7 +130,7 @@ class Solver } } - protected function checkForRootRequireProblems() + protected function checkForRootRequireProblems($ignorePlatformPackageRequirements) { foreach ($this->jobs as $job) { switch ($job['cmd']) { @@ -149,6 +150,10 @@ class Solver break; case 'install': + if ($ignorePlatformPackageRequirements && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { + break; + } + if (!$this->pool->whatProvides($job['packageName'], $job['constraint'])) { $problem = new Problem($this->pool); $problem->addRule(new Rule($this->pool, array(), null, null, $job)); @@ -159,16 +164,24 @@ class Solver } } - public function solve(Request $request) + public function solve(Request $request, $ignorePlatformPackage = false) { $this->jobs = $request->getJobs(); $this->setupInstalledMap(); $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap); - $this->checkForRootRequireProblems(); + $this->checkForRootRequireProblems($ignorePlatformPackage); $this->decisions = new Decisions($this->pool); $this->watchGraph = new RuleWatchGraph; + if ($ignorePlatformPackage) { + foreach ($this->rules as $rule) { + if ($rule->getReason() === Rule::RULE_PACKAGE_REQUIRES && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $rule->getRequiredPackage())) { + $rule->disable(); + } + } + } + foreach ($this->rules as $rule) { $this->watchGraph->insert(new RuleWatchNode($rule)); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2899e2bdb..94b8113c4 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -105,6 +105,7 @@ class Installer protected $verbose = false; protected $update = false; protected $runScripts = true; + protected $ignorePlatformPackage = false; /** * Array of package names/globs flagged for update * @@ -263,7 +264,7 @@ class Installer $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); - $ops = $solver->solve($request); + $ops = $solver->solve($request, $this->ignorePlatformPackage); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $ops); foreach ($ops as $op) { if ($op->getJobType() === 'uninstall') { @@ -470,7 +471,7 @@ class Installer $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); try { - $operations = $solver->solve($request); + $operations = $solver->solve($request, $this->ignorePlatformPackage); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $operations); } catch (SolverProblemsException $e) { $this->io->write('Your requirements could not be resolved to an installable set of packages.'); @@ -1156,6 +1157,18 @@ class Installer return $this; } + /** + * set ignore Platform Package requirements + * + * @param boolean $ignorePlatformPackage + * @return Installer + */ + public function setIgnorePlatformPackage($ignorePlatformPackage = false) { + $this->ignorePlatformPackage = (boolean) $ignorePlatformPackage; + + return $this; + } + /** * restrict the update operation to a few packages, all other packages * that are already installed will be kept at their current version diff --git a/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test b/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test new file mode 100644 index 000000000..88f5d1a5d --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test @@ -0,0 +1,20 @@ +--TEST-- +Install in ignore-platform-package-requirements mode +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "require": { "ext-testdummy": "*" } } + ] + } + ], + "require": { + "a/a": "1.0.0" + } +} +--RUN-- +install --ignore-platform-package-requirements +--EXPECT-- +Installing a/a (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test new file mode 100644 index 000000000..87e4f1297 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test @@ -0,0 +1,24 @@ +--TEST-- +Update in ignore-platform-package-requirements mode +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.1", "require": { "ext-testdummy": "*" } } + ] + } + ], + "require": { + "a/a": "1.0.*" + } +} +--INSTALLED-- +[ + { "name": "a/a", "version": "1.0.0" } +] +--RUN-- +update --ignore-platform-package-requirements +--EXPECT-- +Updating a/a (1.0.0) to a/a (1.0.1) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index a88ce7c21..c31fee0ed 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -204,7 +204,8 @@ class InstallerTest extends TestCase $application->get('install')->setCode(function ($input, $output) use ($installer) { $installer ->setDevMode(!$input->getOption('no-dev')) - ->setDryRun($input->getOption('dry-run')); + ->setDryRun($input->getOption('dry-run')) + ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); return $installer->run(); }); @@ -215,7 +216,8 @@ class InstallerTest extends TestCase ->setUpdate(true) ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) - ->setWhitelistDependencies($input->getOption('with-dependencies')); + ->setWhitelistDependencies($input->getOption('with-dependencies')) + ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); return $installer->run(); }); From 7cea912d30735b83cc9fb401d1ed0e0d6e01064c Mon Sep 17 00:00:00 2001 From: Ala Eddine Khefifi Date: Wed, 8 Oct 2014 11:25:00 +0100 Subject: [PATCH 431/638] minor fixes in DownloadManager.php We should use "===" instead of "==" since it's better and faster in this case. --- src/Composer/Downloader/DownloadManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 01ccc822b..4e03f3fa1 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -202,7 +202,7 @@ class DownloadManager } break; } catch (\RuntimeException $e) { - if ($i == count($sources) - 1) { + if ($i === count($sources) - 1) { throw $e; } From 905f08e46ef1ffdad88eaf598f9546232b97aa4d Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Sat, 11 Oct 2014 16:23:36 +0200 Subject: [PATCH 432/638] Add some examples for "composer" type repositories. These can be used as examples or possibly for tests. --- doc/fixtures/fixtures.md | 17 ++++ .../repo-composer-plain/packages.json | 89 +++++++++++++++++++ ...6adbb14bcd09b8fc8108e3b46e2d73e4c423e.json | 89 +++++++++++++++++++ .../repo-composer-with-includes/packages.json | 10 +++ ...195c9e927eed6628ee1913b8331f97850cc1c.json | 77 ++++++++++++++++ ...b23711c60833672056925a868fe5c6af9c9c2.json | 10 +++ ...b1e29805d822e2f25f8cbc2985f800c0d6d33.json | 22 +++++ .../packages.json | 9 ++ 8 files changed, 323 insertions(+) create mode 100644 doc/fixtures/fixtures.md create mode 100644 doc/fixtures/repo-composer-plain/packages.json create mode 100644 doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json create mode 100644 doc/fixtures/repo-composer-with-includes/packages.json create mode 100644 doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json create mode 100644 doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json create mode 100644 doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json create mode 100644 doc/fixtures/repo-composer-with-providers/packages.json diff --git a/doc/fixtures/fixtures.md b/doc/fixtures/fixtures.md new file mode 100644 index 000000000..d8f33675a --- /dev/null +++ b/doc/fixtures/fixtures.md @@ -0,0 +1,17 @@ +`Composer` type repository fixtures +======================= + +This directory contains some examples of what `composer` type repositories can look like. They serve as illustrating examples accompanying the docs, but can also be used as (initial) fixtures for tests. + +* `repo-composer-plain` is a simple, plain `packages.json` file +* `repo-composer-with-includes` uses the `includes` mechanism +* `repo-composer-with-providers` uses the `providers` mechanism + +Sample Packages used in these fixtures +------- + +All these repositories contain the following packages. + +* `foo/bar` versions 1.0.0, 1.0.1 and 1.1.0; dev-default and 1.0.x-dev branches. On dev-default and in 1.1.0, `bar/baz` ~1.0 is required. +* `qux/quux` only has a dev-default branch. It `replace`s `gar/nix`. + diff --git a/doc/fixtures/repo-composer-plain/packages.json b/doc/fixtures/repo-composer-plain/packages.json new file mode 100644 index 000000000..fab3418ff --- /dev/null +++ b/doc/fixtures/repo-composer-plain/packages.json @@ -0,0 +1,89 @@ +{ + "packages": { + "foo/bar": { + "1.0.0": { + "name": "foo/bar", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "249dec95a52a" + }, + "time": "2014-10-11 15:42:00", + "type": "library" + }, + "1.0.1": { + "name": "foo/bar", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "21e3328295d4" + }, + "time": "2014-10-11 15:45:56", + "type": "library" + }, + "1.0.x-dev": { + "name": "foo/bar", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "14dc17c8e860" + }, + "time": "2014-10-11 15:45:59", + "type": "library" + }, + "1.1.0": { + "name": "foo/bar", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "d2fa3e69ad5b" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:16", + "type": "library" + }, + "dev-default": { + "name": "foo/bar", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "8e5a5c224336" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:18", + "type": "library" + } + }, + "qux/quux": { + "dev-default": { + "name": "qux/quux", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "4a10a567baa5" + }, + "replace": { + "gar/nix": "1.0.*" + }, + "time": "2014-10-11 15:48:15", + "type": "library" + } + } + } +} diff --git a/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json b/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json new file mode 100644 index 000000000..fab3418ff --- /dev/null +++ b/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json @@ -0,0 +1,89 @@ +{ + "packages": { + "foo/bar": { + "1.0.0": { + "name": "foo/bar", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "249dec95a52a" + }, + "time": "2014-10-11 15:42:00", + "type": "library" + }, + "1.0.1": { + "name": "foo/bar", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "21e3328295d4" + }, + "time": "2014-10-11 15:45:56", + "type": "library" + }, + "1.0.x-dev": { + "name": "foo/bar", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "14dc17c8e860" + }, + "time": "2014-10-11 15:45:59", + "type": "library" + }, + "1.1.0": { + "name": "foo/bar", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "d2fa3e69ad5b" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:16", + "type": "library" + }, + "dev-default": { + "name": "foo/bar", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "8e5a5c224336" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:18", + "type": "library" + } + }, + "qux/quux": { + "dev-default": { + "name": "qux/quux", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow", + "reference": "4a10a567baa5" + }, + "replace": { + "gar/nix": "1.0.*" + }, + "time": "2014-10-11 15:48:15", + "type": "library" + } + } + } +} diff --git a/doc/fixtures/repo-composer-with-includes/packages.json b/doc/fixtures/repo-composer-with-includes/packages.json new file mode 100644 index 000000000..7437fa71d --- /dev/null +++ b/doc/fixtures/repo-composer-with-includes/packages.json @@ -0,0 +1,10 @@ +{ + "packages": [ + + ], + "includes": { + "include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json": { + "sha1": "4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e" + } + } +} diff --git a/doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json b/doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json new file mode 100644 index 000000000..eb93bbcd7 --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json @@ -0,0 +1,77 @@ +{ + "packages": { + "foo\/bar": { + "1.0.0": { + "name": "foo\/bar", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "249dec95a52a" + }, + "time": "2014-10-11 15:42:00", + "type": "library", + "uid": 0 + }, + "1.0.1": { + "name": "foo\/bar", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "21e3328295d4" + }, + "time": "2014-10-11 15:45:56", + "type": "library", + "uid": 1 + }, + "1.0.x-dev": { + "name": "foo\/bar", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "14dc17c8e860" + }, + "time": "2014-10-11 15:45:59", + "type": "library", + "uid": 2 + }, + "1.1.0": { + "name": "foo\/bar", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "d2fa3e69ad5b" + }, + "require": { + "bar\/baz": "~1.0" + }, + "time": "2014-10-11 15:43:16", + "type": "library", + "uid": 3 + }, + "dev-default": { + "name": "foo\/bar", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "8e5a5c224336" + }, + "require": { + "bar\/baz": "~1.0" + }, + "time": "2014-10-11 15:43:18", + "type": "library", + "uid": 4 + } + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json b/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json new file mode 100644 index 000000000..74a656c38 --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json @@ -0,0 +1,10 @@ +{ + "providers": { + "foo\/bar": { + "sha256": "fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c" + }, + "qux\/quux": { + "sha256": "301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33" + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json b/doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json new file mode 100644 index 000000000..438b418cd --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json @@ -0,0 +1,22 @@ +{ + "packages": { + "qux\/quux": { + "dev-default": { + "name": "qux\/quux", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow", + "reference": "4a10a567baa5" + }, + "replace": { + "gar\/nix": "1.0.*" + }, + "time": "2014-10-11 15:48:15", + "type": "library", + "uid": 5 + } + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/packages.json b/doc/fixtures/repo-composer-with-providers/packages.json new file mode 100644 index 000000000..c9e5c2e20 --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/packages.json @@ -0,0 +1,9 @@ +{ + "packages": [], + "providers-url": "\/p\/%package%$%hash%.json", + "provider-includes": { + "p\/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json": { + "sha256": "d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2" + } + } +} \ No newline at end of file From c8b9d299d5f90cfe3056d01d3c110ffa3cbe9546 Mon Sep 17 00:00:00 2001 From: jakoch Date: Sat, 11 Oct 2014 19:30:19 +0200 Subject: [PATCH 433/638] added $installer->isDryRun() --- src/Composer/Installer.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2899e2bdb..e70eb537f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1052,6 +1052,16 @@ class Installer return $this; } + /** + * Checks, if this is a dry run (simulation mode). + * + * @return bool + */ + public function isDryRun() + { + return $this->dryRun; + } + /** * prefer source installation * From 24b4ce8831ef2f1f78fd3a9bed3ea21860c6732b Mon Sep 17 00:00:00 2001 From: jakoch Date: Sat, 11 Oct 2014 19:46:37 +0200 Subject: [PATCH 434/638] added $installer->isVerbose() --- src/Composer/Installer.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e70eb537f..071b1d4fc 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1166,6 +1166,16 @@ class Installer return $this; } + /** + * Checks, if running in verbose mode. + * + * @return bool + */ + public function isVerbose() + { + return $this->verbose; + } + /** * restrict the update operation to a few packages, all other packages * that are already installed will be kept at their current version From 579409e6c59dee4ce7fe71e52267eb1b1dcbd540 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Mon, 13 Oct 2014 12:48:40 +0200 Subject: [PATCH 435/638] Update fixtures to include gar/nix (which is being replaced) and bar/baz (which has a branch-alias) --- doc/fixtures/fixtures.md | 3 + .../repo-composer-plain/packages.json | 81 ++++++++- ...6adbb14bcd09b8fc8108e3b46e2d73e4c423e.json | 89 ---------- ...86b937f0502d92f776072cd49c002dca742b9.json | 158 ++++++++++++++++++ .../repo-composer-with-includes/packages.json | 4 +- ...156dd4d0821a97fd3e428bc910833e3e46dbe.json | 50 ++++++ ...38e5f3b79029c1f8d9ab5b477ea15776ba0a.json} | 20 +-- ...67b9bea558b971e5b082f330ae4f1d484c321.json | 50 ++++++ ...791c612db0c05e22d90e9286e233cacd86ed8.json | 16 ++ ...b23711c60833672056925a868fe5c6af9c9c2.json | 10 -- ...601923a5a00ccc5fcce50a77ecdd461eb72d.json} | 4 +- .../packages.json | 4 +- 12 files changed, 368 insertions(+), 121 deletions(-) delete mode 100644 doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json create mode 100644 doc/fixtures/repo-composer-with-includes/include/all$5fa86b937f0502d92f776072cd49c002dca742b9.json create mode 100644 doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json rename doc/fixtures/repo-composer-with-providers/p/foo/{bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json => bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json} (95%) create mode 100644 doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json create mode 100644 doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json delete mode 100644 doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json rename doc/fixtures/repo-composer-with-providers/p/qux/{quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json => quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json} (94%) diff --git a/doc/fixtures/fixtures.md b/doc/fixtures/fixtures.md index d8f33675a..fd77f3c37 100644 --- a/doc/fixtures/fixtures.md +++ b/doc/fixtures/fixtures.md @@ -14,4 +14,7 @@ All these repositories contain the following packages. * `foo/bar` versions 1.0.0, 1.0.1 and 1.1.0; dev-default and 1.0.x-dev branches. On dev-default and in 1.1.0, `bar/baz` ~1.0 is required. * `qux/quux` only has a dev-default branch. It `replace`s `gar/nix`. +* `gar/nix` has a 1.0.0 version and a dev-default branch. It is being replaced by `qux/quux`. +* `bar/baz` has a 1.0.0 version and 1.0.x-dev as well as dev-default branches. Additionally, 1.1.x-dev is a branch alias for dev-default. + diff --git a/doc/fixtures/repo-composer-plain/packages.json b/doc/fixtures/repo-composer-plain/packages.json index fab3418ff..219051998 100644 --- a/doc/fixtures/repo-composer-plain/packages.json +++ b/doc/fixtures/repo-composer-plain/packages.json @@ -1,5 +1,48 @@ { "packages": { + "bar/baz": { + "1.0.0": { + "name": "bar/baz", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "35810817c14d" + }, + "time": "2014-10-13 12:04:55", + "type": "library" + }, + "1.0.x-dev": { + "name": "bar/baz", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "ffff9aae6ed5" + }, + "time": "2014-10-13 12:05:37", + "type": "library" + }, + "dev-default": { + "name": "bar/baz", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "f317e556f2e2" + }, + "time": "2014-10-13 12:06:45", + "type": "library", + "extra": { + "branch-alias": { + "dev-default": "1.1.x-dev" + } + } + } + }, "foo/bar": { "1.0.0": { "name": "foo/bar", @@ -7,7 +50,7 @@ "version_normalized": "1.0.0.0", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "249dec95a52a" }, "time": "2014-10-11 15:42:00", @@ -19,7 +62,7 @@ "version_normalized": "1.0.1.0", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "21e3328295d4" }, "time": "2014-10-11 15:45:56", @@ -31,7 +74,7 @@ "version_normalized": "1.0.9999999.9999999-dev", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "14dc17c8e860" }, "time": "2014-10-11 15:45:59", @@ -43,7 +86,7 @@ "version_normalized": "1.1.0.0", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "d2fa3e69ad5b" }, "require": { @@ -58,7 +101,7 @@ "version_normalized": "9999999-dev", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "8e5a5c224336" }, "require": { @@ -68,6 +111,32 @@ "type": "library" } }, + "gar/nix": { + "1.0.0": { + "name": "gar/nix", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "44977145d64e" + }, + "time": "2014-10-13 12:03:33", + "type": "library" + }, + "dev-default": { + "name": "gar/nix", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "51cca95a31c2" + }, + "time": "2014-10-13 12:03:35", + "type": "library" + } + }, "qux/quux": { "dev-default": { "name": "qux/quux", @@ -75,7 +144,7 @@ "version_normalized": "9999999-dev", "source": { "type": "hg", - "url": "http://some.where/over/the/rainbow", + "url": "http://some.where/over/the/rainbow/", "reference": "4a10a567baa5" }, "replace": { diff --git a/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json b/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json deleted file mode 100644 index fab3418ff..000000000 --- a/doc/fixtures/repo-composer-with-includes/include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "packages": { - "foo/bar": { - "1.0.0": { - "name": "foo/bar", - "version": "1.0.0", - "version_normalized": "1.0.0.0", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "249dec95a52a" - }, - "time": "2014-10-11 15:42:00", - "type": "library" - }, - "1.0.1": { - "name": "foo/bar", - "version": "1.0.1", - "version_normalized": "1.0.1.0", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "21e3328295d4" - }, - "time": "2014-10-11 15:45:56", - "type": "library" - }, - "1.0.x-dev": { - "name": "foo/bar", - "version": "1.0.x-dev", - "version_normalized": "1.0.9999999.9999999-dev", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "14dc17c8e860" - }, - "time": "2014-10-11 15:45:59", - "type": "library" - }, - "1.1.0": { - "name": "foo/bar", - "version": "1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "d2fa3e69ad5b" - }, - "require": { - "bar/baz": "~1.0" - }, - "time": "2014-10-11 15:43:16", - "type": "library" - }, - "dev-default": { - "name": "foo/bar", - "version": "dev-default", - "version_normalized": "9999999-dev", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "8e5a5c224336" - }, - "require": { - "bar/baz": "~1.0" - }, - "time": "2014-10-11 15:43:18", - "type": "library" - } - }, - "qux/quux": { - "dev-default": { - "name": "qux/quux", - "version": "dev-default", - "version_normalized": "9999999-dev", - "source": { - "type": "hg", - "url": "http://some.where/over/the/rainbow", - "reference": "4a10a567baa5" - }, - "replace": { - "gar/nix": "1.0.*" - }, - "time": "2014-10-11 15:48:15", - "type": "library" - } - } - } -} diff --git a/doc/fixtures/repo-composer-with-includes/include/all$5fa86b937f0502d92f776072cd49c002dca742b9.json b/doc/fixtures/repo-composer-with-includes/include/all$5fa86b937f0502d92f776072cd49c002dca742b9.json new file mode 100644 index 000000000..219051998 --- /dev/null +++ b/doc/fixtures/repo-composer-with-includes/include/all$5fa86b937f0502d92f776072cd49c002dca742b9.json @@ -0,0 +1,158 @@ +{ + "packages": { + "bar/baz": { + "1.0.0": { + "name": "bar/baz", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "35810817c14d" + }, + "time": "2014-10-13 12:04:55", + "type": "library" + }, + "1.0.x-dev": { + "name": "bar/baz", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "ffff9aae6ed5" + }, + "time": "2014-10-13 12:05:37", + "type": "library" + }, + "dev-default": { + "name": "bar/baz", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "f317e556f2e2" + }, + "time": "2014-10-13 12:06:45", + "type": "library", + "extra": { + "branch-alias": { + "dev-default": "1.1.x-dev" + } + } + } + }, + "foo/bar": { + "1.0.0": { + "name": "foo/bar", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "249dec95a52a" + }, + "time": "2014-10-11 15:42:00", + "type": "library" + }, + "1.0.1": { + "name": "foo/bar", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "21e3328295d4" + }, + "time": "2014-10-11 15:45:56", + "type": "library" + }, + "1.0.x-dev": { + "name": "foo/bar", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "14dc17c8e860" + }, + "time": "2014-10-11 15:45:59", + "type": "library" + }, + "1.1.0": { + "name": "foo/bar", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "d2fa3e69ad5b" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:16", + "type": "library" + }, + "dev-default": { + "name": "foo/bar", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "8e5a5c224336" + }, + "require": { + "bar/baz": "~1.0" + }, + "time": "2014-10-11 15:43:18", + "type": "library" + } + }, + "gar/nix": { + "1.0.0": { + "name": "gar/nix", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "44977145d64e" + }, + "time": "2014-10-13 12:03:33", + "type": "library" + }, + "dev-default": { + "name": "gar/nix", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "51cca95a31c2" + }, + "time": "2014-10-13 12:03:35", + "type": "library" + } + }, + "qux/quux": { + "dev-default": { + "name": "qux/quux", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http://some.where/over/the/rainbow/", + "reference": "4a10a567baa5" + }, + "replace": { + "gar/nix": "1.0.*" + }, + "time": "2014-10-11 15:48:15", + "type": "library" + } + } + } +} diff --git a/doc/fixtures/repo-composer-with-includes/packages.json b/doc/fixtures/repo-composer-with-includes/packages.json index 7437fa71d..1ef97530a 100644 --- a/doc/fixtures/repo-composer-with-includes/packages.json +++ b/doc/fixtures/repo-composer-with-includes/packages.json @@ -3,8 +3,8 @@ ], "includes": { - "include/all$4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e.json": { - "sha1": "4a06adbb14bcd09b8fc8108e3b46e2d73e4c423e" + "include/all$5fa86b937f0502d92f776072cd49c002dca742b9.json": { + "sha1": "5fa86b937f0502d92f776072cd49c002dca742b9" } } } diff --git a/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json b/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json new file mode 100644 index 000000000..94a43b019 --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json @@ -0,0 +1,50 @@ +{ + "packages": { + "bar\/baz": { + "1.0.0": { + "name": "bar\/baz", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "35810817c14d" + }, + "time": "2014-10-13 12:04:55", + "type": "library", + "uid": 0 + }, + "1.0.x-dev": { + "name": "bar\/baz", + "version": "1.0.x-dev", + "version_normalized": "1.0.9999999.9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "ffff9aae6ed5" + }, + "time": "2014-10-13 12:05:37", + "type": "library", + "uid": 1 + }, + "dev-default": { + "name": "bar\/baz", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "f317e556f2e2" + }, + "time": "2014-10-13 12:06:45", + "type": "library", + "extra": { + "branch-alias": { + "dev-default": "1.1.x-dev" + } + }, + "uid": 2 + } + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json b/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json similarity index 95% rename from doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json rename to doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json index eb93bbcd7..7dc7cc91e 100644 --- a/doc/fixtures/repo-composer-with-providers/p/foo/bar$fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c.json +++ b/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json @@ -7,12 +7,12 @@ "version_normalized": "1.0.0.0", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "249dec95a52a" }, "time": "2014-10-11 15:42:00", "type": "library", - "uid": 0 + "uid": 3 }, "1.0.1": { "name": "foo\/bar", @@ -20,12 +20,12 @@ "version_normalized": "1.0.1.0", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "21e3328295d4" }, "time": "2014-10-11 15:45:56", "type": "library", - "uid": 1 + "uid": 4 }, "1.0.x-dev": { "name": "foo\/bar", @@ -33,12 +33,12 @@ "version_normalized": "1.0.9999999.9999999-dev", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "14dc17c8e860" }, "time": "2014-10-11 15:45:59", "type": "library", - "uid": 2 + "uid": 5 }, "1.1.0": { "name": "foo\/bar", @@ -46,7 +46,7 @@ "version_normalized": "1.1.0.0", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "d2fa3e69ad5b" }, "require": { @@ -54,7 +54,7 @@ }, "time": "2014-10-11 15:43:16", "type": "library", - "uid": 3 + "uid": 6 }, "dev-default": { "name": "foo\/bar", @@ -62,7 +62,7 @@ "version_normalized": "9999999-dev", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "8e5a5c224336" }, "require": { @@ -70,7 +70,7 @@ }, "time": "2014-10-11 15:43:18", "type": "library", - "uid": 4 + "uid": 7 } } } diff --git a/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json b/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json new file mode 100644 index 000000000..512b8d882 --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json @@ -0,0 +1,50 @@ +{ + "packages": { + "qux\/quux": { + "dev-default": { + "name": "qux\/quux", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "4a10a567baa5" + }, + "replace": { + "gar\/nix": "1.0.*" + }, + "time": "2014-10-11 15:48:15", + "type": "library", + "uid": 10 + } + }, + "gar\/nix": { + "1.0.0": { + "name": "gar\/nix", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "44977145d64e" + }, + "time": "2014-10-13 12:03:33", + "type": "library", + "uid": 8 + }, + "dev-default": { + "name": "gar\/nix", + "version": "dev-default", + "version_normalized": "9999999-dev", + "source": { + "type": "hg", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", + "reference": "51cca95a31c2" + }, + "time": "2014-10-13 12:03:35", + "type": "library", + "uid": 9 + } + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json b/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json new file mode 100644 index 000000000..b82eb418e --- /dev/null +++ b/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json @@ -0,0 +1,16 @@ +{ + "providers": { + "bar\/baz": { + "sha256": "923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe" + }, + "foo\/bar": { + "sha256": "4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a" + }, + "gar\/nix": { + "sha256": "5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321" + }, + "qux\/quux": { + "sha256": "c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d" + } + } +} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json b/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json deleted file mode 100644 index 74a656c38..000000000 --- a/doc/fixtures/repo-composer-with-providers/p/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "providers": { - "foo\/bar": { - "sha256": "fc231cb99b4427d2e6e10e500e3195c9e927eed6628ee1913b8331f97850cc1c" - }, - "qux\/quux": { - "sha256": "301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33" - } - } -} \ No newline at end of file diff --git a/doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json b/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json similarity index 94% rename from doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json rename to doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json index 438b418cd..014187206 100644 --- a/doc/fixtures/repo-composer-with-providers/p/qux/quux$301aa898cc49a14ebe56084431db1e29805d822e2f25f8cbc2985f800c0d6d33.json +++ b/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json @@ -7,7 +7,7 @@ "version_normalized": "9999999-dev", "source": { "type": "hg", - "url": "http:\/\/some.where\/over\/the\/rainbow", + "url": "http:\/\/some.where\/over\/the\/rainbow\/", "reference": "4a10a567baa5" }, "replace": { @@ -15,7 +15,7 @@ }, "time": "2014-10-11 15:48:15", "type": "library", - "uid": 5 + "uid": 10 } } } diff --git a/doc/fixtures/repo-composer-with-providers/packages.json b/doc/fixtures/repo-composer-with-providers/packages.json index c9e5c2e20..65968a861 100644 --- a/doc/fixtures/repo-composer-with-providers/packages.json +++ b/doc/fixtures/repo-composer-with-providers/packages.json @@ -2,8 +2,8 @@ "packages": [], "providers-url": "\/p\/%package%$%hash%.json", "provider-includes": { - "p\/provider-active$d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2.json": { - "sha256": "d0a9908f6006b833fa0b654dc47b23711c60833672056925a868fe5c6af9c9c2" + "p\/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json": { + "sha256": "1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8" } } } \ No newline at end of file From 225a8f7a5660e12ad53298859a6674293cca3cb9 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Mon, 13 Oct 2014 16:17:28 -0300 Subject: [PATCH 436/638] Added branch-alias for dev-master to ```--version|-V``` output when current version is actually a revision (a non release/git tag). Example: Before: ``` $ composer --version Composer version 73e9db5d9952d52a46ecbc20a269a8c5f9c5b011 2014-10-07 15:03:19 ``` After: ``` $ composer --version Composer version 1.0-dev (73e9db5d9952d52a46ecbc20a269a8c5f9c5b011) 2014-10-07 15:03:19 ``` | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Doc PR | none --- src/Composer/Compiler.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 41c30d6d1..84dfec94d 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -12,6 +12,7 @@ namespace Composer; +use Composer\Json\JsonFile; use Symfony\Component\Finder\Finder; use Symfony\Component\Process\Process; @@ -44,6 +45,14 @@ class Compiler } $this->version = trim($process->getOutput()); + $localConfig = __DIR__.'/../../composer.json'; + $file = new JsonFile($localConfig); + $localConfig = $file->read(); + + if (isset($localConfig['extra']['branch-alias']['dev-master'])) { + $this->version = sprintf('%s (%s)', $localConfig['extra']['branch-alias']['dev-master'], $this->version); + } + $process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__); if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); From b0cabd1e989ac7a1fdaac51610e987a833c262e1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Oct 2014 08:43:09 +0100 Subject: [PATCH 437/638] Revert "Added branch-alias for dev-master to ```--version|-V``` output when current", fixes #3356 This reverts commit 225a8f7a5660e12ad53298859a6674293cca3cb9. --- src/Composer/Compiler.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 84dfec94d..41c30d6d1 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -12,7 +12,6 @@ namespace Composer; -use Composer\Json\JsonFile; use Symfony\Component\Finder\Finder; use Symfony\Component\Process\Process; @@ -45,14 +44,6 @@ class Compiler } $this->version = trim($process->getOutput()); - $localConfig = __DIR__.'/../../composer.json'; - $file = new JsonFile($localConfig); - $localConfig = $file->read(); - - if (isset($localConfig['extra']['branch-alias']['dev-master'])) { - $this->version = sprintf('%s (%s)', $localConfig['extra']['branch-alias']['dev-master'], $this->version); - } - $process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__); if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); From e36f2ea618642f803faa2d2e5c8484ce5591b211 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Thu, 16 Oct 2014 22:13:17 -0300 Subject: [PATCH 438/638] Added branch-alias for dev-master to --version|-V output when current version is actually a revision (a non release/git tag). This replies the work done in #3352 respecting other uses of ```Composer::VERSION``` constant. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | Doc PR | none --- src/Composer/Compiler.php | 10 ++++++++++ src/Composer/Composer.php | 1 + src/Composer/Console/Application.php | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 41c30d6d1..20989cf94 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -12,6 +12,7 @@ namespace Composer; +use Composer\Json\JsonFile; use Symfony\Component\Finder\Finder; use Symfony\Component\Process\Process; @@ -24,6 +25,7 @@ use Symfony\Component\Process\Process; class Compiler { private $version; + private $branchAliasVersion = ''; private $versionDate; /** @@ -48,6 +50,13 @@ class Compiler if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); } + $localConfig = __DIR__.'/../../composer.json'; + $file = new JsonFile($localConfig); + $localConfig = $file->read(); + + if (isset($localConfig['extra']['branch-alias']['dev-master'])) { + $this->branchAliasVersion = $localConfig['extra']['branch-alias']['dev-master']; + } $date = new \DateTime(trim($process->getOutput())); $date->setTimezone(new \DateTimeZone('UTC')); $this->versionDate = $date->format('Y-m-d H:i:s'); @@ -138,6 +147,7 @@ class Compiler if ($path === 'src/Composer/Composer.php') { $content = str_replace('@package_version@', $this->version, $content); + $content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content); $content = str_replace('@release_date@', $this->versionDate, $content); } diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 6731ea9a4..c874a0796 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -29,6 +29,7 @@ use Composer\Autoload\AutoloadGenerator; class Composer { const VERSION = '@package_version@'; + const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@'; const RELEASE_DATE = '@release_date@'; /** diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 7148a9a11..fa866c247 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -265,6 +265,16 @@ class Application extends BaseApplication */ public function getLongVersion() { + if (Composer::BRANCH_ALIAS_VERSION) { + return sprintf( + '%s version %s (%s) %s', + $this->getName(), + Composer::BRANCH_ALIAS_VERSION, + $this->getVersion(), + Composer::RELEASE_DATE + ); + } + return parent::getLongVersion() . ' ' . Composer::RELEASE_DATE; } From f8fae61b056c80c326a1a9d25176a8eb2c2ae9da Mon Sep 17 00:00:00 2001 From: aaukt Date: Fri, 17 Oct 2014 09:10:47 +0200 Subject: [PATCH 439/638] Add fallback for findPackage(s) for repo without provider This was introcuced in a4d43ee8605e8c48d8a8b277720dea9f92119d82, but is missing the fallback for a repository without providers. --- src/Composer/Repository/ComposerRepository.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 82d8b9a36..134a47c2a 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -103,6 +103,9 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository */ public function findPackage($name, $version) { + if (!$this->hasProviders()) { + return parent::findPackage($name, $version); + } // normalize version & name $versionParser = new VersionParser(); $version = $versionParser->normalize($version); @@ -125,6 +128,9 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository */ public function findPackages($name, $version = null) { + if (!$this->hasProviders()) { + return parent::findPackages($name, $version); + } // normalize name $name = strtolower($name); From ec0463a40055fd678d02d900b3f90acd5f72b9e1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 15:26:00 +0100 Subject: [PATCH 440/638] Add tests for platform packages required by root and avoid creating rules altogether instead of disabling them, refs #3320 --- src/Composer/Command/InstallCommand.php | 4 +-- src/Composer/Command/UpdateCommand.php | 4 +-- src/Composer/DependencyResolver/Request.php | 13 ++++++++- .../DependencyResolver/RuleSetGenerator.php | 27 ++++++++++++------- src/Composer/DependencyResolver/Solver.php | 18 ++++--------- src/Composer/Installer.php | 18 ++++++++----- .../Test/DependencyResolver/RequestTest.php | 10 +++---- ...-ignore-platform-package-requirements.test | 10 ++++--- ...-ignore-platform-package-requirements.test | 8 +++--- tests/Composer/Test/InstallerTest.php | 4 +-- 10 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index b65e08f0f..43a169e7d 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -45,7 +45,7 @@ class InstallCommand extends Command new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), - new InputOption('ignore-platform-package-requirements', null, InputOption::VALUE_NONE, 'Ignore PHP Extention requirements.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp(<<setDevMode(!$input->getOption('no-dev')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) - ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 131390dd7..9d1e95dd0 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -46,7 +46,7 @@ class UpdateCommand extends Command new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), - new InputOption('ignore-platform-package-requirements', null, InputOption::VALUE_NONE, 'Ignore PHP Extention requirements.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), )) ->setHelp(<<update
command reads the composer.json file from the @@ -120,7 +120,7 @@ EOT ->setUpdate(true) ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) - ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/DependencyResolver/Request.php b/src/Composer/DependencyResolver/Request.php index bf74318f6..79904a3a1 100644 --- a/src/Composer/DependencyResolver/Request.php +++ b/src/Composer/DependencyResolver/Request.php @@ -43,7 +43,17 @@ class Request $this->addJob($packageName, 'remove', $constraint); } - protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null) + /** + * Mark an existing package as being installed and having to remain installed + * + * These jobs will not be tempered with by the solver + */ + public function fix($packageName, LinkConstraintInterface $constraint = null) + { + $this->addJob($packageName, 'install', $constraint, true); + } + + protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null, $fixed = false) { $packageName = strtolower($packageName); @@ -51,6 +61,7 @@ class Request 'cmd' => $cmd, 'packageName' => $packageName, 'constraint' => $constraint, + 'fixed' => $fixed ); } diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 5bcf9a079..fa58687ea 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -14,6 +14,7 @@ namespace Composer\DependencyResolver; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; +use Composer\Repository\PlatformRepository; /** * @author Nils Adermann @@ -178,7 +179,7 @@ class RuleSetGenerator } } - protected function addRulesForPackage(PackageInterface $package) + protected function addRulesForPackage(PackageInterface $package, $ignorePlatformReqs) { $workQueue = new \SplQueue; $workQueue->enqueue($package); @@ -192,6 +193,10 @@ class RuleSetGenerator $this->addedMap[$package->getId()] = true; foreach ($package->getRequires() as $link) { + if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + continue; + } + $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link)); @@ -264,12 +269,12 @@ class RuleSetGenerator * @param PackageInterface $package Rules for this package's updates are to * be added */ - private function addRulesForUpdatePackages(PackageInterface $package) + private function addRulesForUpdatePackages(PackageInterface $package, $ignorePlatformReqs) { $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package); foreach ($updates as $update) { - $this->addRulesForPackage($update); + $this->addRulesForPackage($update, $ignorePlatformReqs); } } @@ -296,16 +301,20 @@ class RuleSetGenerator } } - protected function addRulesForJobs() + protected function addRulesForJobs($ignorePlatformReqs) { foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'install': + if (!$job['fixed'] && $ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { + continue; + } + $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); if ($packages) { foreach ($packages as $package) { if (!isset($this->installedMap[$package->getId()])) { - $this->addRulesForPackage($package); + $this->addRulesForPackage($package, $ignorePlatformReqs); } } @@ -326,7 +335,7 @@ class RuleSetGenerator } } - public function getRulesFor($jobs, $installedMap) + public function getRulesFor($jobs, $installedMap, $ignorePlatformReqs = false) { $this->jobs = $jobs; $this->rules = new RuleSet; @@ -343,11 +352,11 @@ class RuleSetGenerator $this->addedMap = array(); foreach ($this->installedMap as $package) { - $this->addRulesForPackage($package); - $this->addRulesForUpdatePackages($package); + $this->addRulesForPackage($package, $ignorePlatformReqs); + $this->addRulesForUpdatePackages($package, $ignorePlatformReqs); } - $this->addRulesForJobs(); + $this->addRulesForJobs($ignorePlatformReqs); return $this->rules; } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index c6ad2b17a..2a0256e40 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -130,7 +130,7 @@ class Solver } } - protected function checkForRootRequireProblems($ignorePlatformPackageRequirements) + protected function checkForRootRequireProblems($ignorePlatformReqs) { foreach ($this->jobs as $job) { switch ($job['cmd']) { @@ -150,7 +150,7 @@ class Solver break; case 'install': - if ($ignorePlatformPackageRequirements && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { + if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { break; } @@ -164,24 +164,16 @@ class Solver } } - public function solve(Request $request, $ignorePlatformPackage = false) + public function solve(Request $request, $ignorePlatformReqs = false) { $this->jobs = $request->getJobs(); $this->setupInstalledMap(); - $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap); - $this->checkForRootRequireProblems($ignorePlatformPackage); + $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs); + $this->checkForRootRequireProblems($ignorePlatformReqs); $this->decisions = new Decisions($this->pool); $this->watchGraph = new RuleWatchGraph; - if ($ignorePlatformPackage) { - foreach ($this->rules as $rule) { - if ($rule->getReason() === Rule::RULE_PACKAGE_REQUIRES && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $rule->getRequiredPackage())) { - $rule->disable(); - } - } - } - foreach ($this->rules as $rule) { $this->watchGraph->insert(new RuleWatchNode($rule)); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 6c232a060..4fb58e07f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -105,7 +105,7 @@ class Installer protected $verbose = false; protected $update = false; protected $runScripts = true; - protected $ignorePlatformPackage = false; + protected $ignorePlatformReqs = false; /** * Array of package names/globs flagged for update * @@ -264,7 +264,7 @@ class Installer $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); - $ops = $solver->solve($request, $this->ignorePlatformPackage); + $ops = $solver->solve($request, $this->ignorePlatformReqs); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $ops); foreach ($ops as $op) { if ($op->getJobType() === 'uninstall') { @@ -471,7 +471,7 @@ class Installer $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request); $solver = new Solver($policy, $pool, $installedRepo); try { - $operations = $solver->solve($request, $this->ignorePlatformPackage); + $operations = $solver->solve($request, $this->ignorePlatformReqs); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $operations); } catch (SolverProblemsException $e) { $this->io->write('Your requirements could not be resolved to an installable set of packages.'); @@ -658,6 +658,10 @@ class Installer } $rootConstraints = array(); foreach ($requires as $req => $constraint) { + // skip platform requirements from the root package to avoid filtering out existing platform packages + if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { + continue; + } $rootConstraints[$req] = $constraint->getConstraint(); } @@ -705,7 +709,7 @@ class Installer || !isset($provided[$package->getName()]) || !$provided[$package->getName()]->getConstraint()->matches($constraint) ) { - $request->install($package->getName(), $constraint); + $request->fix($package->getName(), $constraint); } } @@ -1180,12 +1184,12 @@ class Installer /** * set ignore Platform Package requirements * - * @param boolean $ignorePlatformPackage + * @param boolean $ignorePlatformReqs * @return Installer */ - public function setIgnorePlatformPackage($ignorePlatformPackage = false) + public function setIgnorePlatformRequirements($ignorePlatformReqs = false) { - $this->ignorePlatformPackage = (boolean) $ignorePlatformPackage; + $this->ignorePlatformReqs = (boolean) $ignorePlatformReqs; return $this; } diff --git a/tests/Composer/Test/DependencyResolver/RequestTest.php b/tests/Composer/Test/DependencyResolver/RequestTest.php index 0ba43ca73..78a1da962 100644 --- a/tests/Composer/Test/DependencyResolver/RequestTest.php +++ b/tests/Composer/Test/DependencyResolver/RequestTest.php @@ -34,14 +34,14 @@ class RequestTest extends TestCase $request = new Request($pool); $request->install('foo'); - $request->install('bar'); + $request->fix('bar'); $request->remove('foobar'); $this->assertEquals( array( - array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => null), - array('cmd' => 'install', 'packageName' => 'bar', 'constraint' => null), - array('cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null), + array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => null, 'fixed' => false), + array('cmd' => 'install', 'packageName' => 'bar', 'constraint' => null, 'fixed' => true), + array('cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null, 'fixed' => false), ), $request->getJobs()); } @@ -66,7 +66,7 @@ class RequestTest extends TestCase $this->assertEquals( array( - array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint), + array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint, 'fixed' => false), ), $request->getJobs() ); diff --git a/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test b/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test index 88f5d1a5d..7959b6e07 100644 --- a/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test +++ b/tests/Composer/Test/Fixtures/installer/install-ignore-platform-package-requirements.test @@ -1,20 +1,22 @@ --TEST-- -Install in ignore-platform-package-requirements mode +Install in ignore-platform-reqs mode --COMPOSER-- { "repositories": [ { "type": "package", "package": [ - { "name": "a/a", "version": "1.0.0", "require": { "ext-testdummy": "*" } } + { "name": "a/a", "version": "1.0.0", "require": { "ext-testdummy": "*", "php": "98" } } ] } ], "require": { - "a/a": "1.0.0" + "a/a": "1.0.0", + "php": "99.9", + "ext-dummy2": "3" } } --RUN-- -install --ignore-platform-package-requirements +install --ignore-platform-reqs --EXPECT-- Installing a/a (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test index 87e4f1297..02f94cd6e 100644 --- a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test +++ b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test @@ -1,5 +1,5 @@ --TEST-- -Update in ignore-platform-package-requirements mode +Update in ignore-platform-reqs mode --COMPOSER-- { "repositories": [ @@ -11,7 +11,9 @@ Update in ignore-platform-package-requirements mode } ], "require": { - "a/a": "1.0.*" + "a/a": "1.0.*", + "php": "99.9", + "ext-dummy2": "9" } } --INSTALLED-- @@ -19,6 +21,6 @@ Update in ignore-platform-package-requirements mode { "name": "a/a", "version": "1.0.0" } ] --RUN-- -update --ignore-platform-package-requirements +update --ignore-platform-reqs --EXPECT-- Updating a/a (1.0.0) to a/a (1.0.1) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index c31fee0ed..cc12cb3ef 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -205,7 +205,7 @@ class InstallerTest extends TestCase $installer ->setDevMode(!$input->getOption('no-dev')) ->setDryRun($input->getOption('dry-run')) - ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); return $installer->run(); }); @@ -217,7 +217,7 @@ class InstallerTest extends TestCase ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) - ->setIgnorePlatformPackage($input->getOption('ignore-platform-package-requirements')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); return $installer->run(); }); From 97f191a7a78bc72a72469ddb709632b929265415 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 15:37:37 +0100 Subject: [PATCH 441/638] Add --ignore-platform-reqs to docs, refs #3320 --- doc/03-cli.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 2dd0a6c36..786285922 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -80,6 +80,8 @@ resolution. servers and other use cases where you typically do not run updates of the vendors. It is also a way to circumvent problems with git if you do not have a proper setup. +* **--ignore-platform-reqs:** ignore `php`, `hhvm` and `ext-*` requirements + and force the installation even if the local machine does not fulfill these. * **--dry-run:** If you want to run through an installation without actually installing a package, you can use `--dry-run`. This will simulate the installation and show you what would happen. @@ -121,6 +123,8 @@ php composer.phar update vendor/* * **--prefer-source:** Install packages from `source` when available. * **--prefer-dist:** Install packages from `dist` when available. +* **--ignore-platform-reqs:** ignore `php`, `hhvm` and `ext-*` requirements + and force the installation even if the local machine does not fulfill these. * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. From 8a29c812e3464a6c4dacdc8fc6fdade57bfab8a5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 15:52:44 +0100 Subject: [PATCH 442/638] Fix docs --- doc/03-cli.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 786285922..23fdbebd8 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -80,8 +80,9 @@ resolution. servers and other use cases where you typically do not run updates of the vendors. It is also a way to circumvent problems with git if you do not have a proper setup. -* **--ignore-platform-reqs:** ignore `php`, `hhvm` and `ext-*` requirements - and force the installation even if the local machine does not fulfill these. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and force the installation even if the local machine does not + fulfill these. * **--dry-run:** If you want to run through an installation without actually installing a package, you can use `--dry-run`. This will simulate the installation and show you what would happen. @@ -123,8 +124,9 @@ php composer.phar update vendor/* * **--prefer-source:** Install packages from `source` when available. * **--prefer-dist:** Install packages from `dist` when available. -* **--ignore-platform-reqs:** ignore `php`, `hhvm` and `ext-*` requirements - and force the installation even if the local machine does not fulfill these. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and force the installation even if the local machine does not + fulfill these. * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. From 9c32f24cfcfcece988726970ced92caf6fe4b4f7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 16:06:01 +0100 Subject: [PATCH 443/638] Remove original indent if present, fixes #3143 --- src/Composer/Command/StatusCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/StatusCommand.php b/src/Composer/Command/StatusCommand.php index a125bdd3c..65662c048 100644 --- a/src/Composer/Command/StatusCommand.php +++ b/src/Composer/Command/StatusCommand.php @@ -84,7 +84,7 @@ EOT foreach ($errors as $path => $changes) { if ($input->getOption('verbose')) { $indentedChanges = implode("\n", array_map(function ($line) { - return ' ' . $line; + return ' ' . ltrim($line); }, explode("\n", $changes))); $output->writeln(''.$path.':'); $output->writeln($indentedChanges); From 332a933e8b744901441f1e80b46f36c7e4f24f04 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 16:32:48 +0100 Subject: [PATCH 444/638] Remove fallback on missing commit, fixes #3314, fixes #3147 --- src/Composer/Downloader/DownloadManager.php | 4 +-- src/Composer/Downloader/GitDownloader.php | 38 ++------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 01ccc822b..3460026cf 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -192,7 +192,7 @@ class DownloadManager foreach ($sources as $i => $source) { if (isset($e)) { - $this->io->write('Now trying to download from ' . $source . ''); + $this->io->write(' Now trying to download from ' . $source . ''); } $package->setInstallationSource($source); try { @@ -207,7 +207,7 @@ class DownloadManager } $this->io->write( - 'Failed to download '. + ' Failed to download '. $package->getPrettyName(). ' from ' . $source . ': '. $e->getMessage().'' diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 0584f675a..503d792a1 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -256,42 +256,8 @@ class GitDownloader extends VcsDownloader } // reference was not found (prints "fatal: reference is not a tree: $ref") - if ($date && false !== strpos($this->process->getErrorOutput(), $reference)) { - $date = $date->format('U'); - - // guess which remote branch to look at first - $command = 'git branch -r'; - if (0 !== $this->process->execute($command, $output, $path)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); - } - - $guessTemplate = 'git log --until=%s --date=raw -n1 --pretty=%%H %s'; - foreach ($this->process->splitLines($output) as $line) { - if (preg_match('{^composer/'.preg_quote($branch).'(?:\.x)?$}i', trim($line))) { - // find the previous commit by date in the given branch - if (0 === $this->process->execute(sprintf($guessTemplate, $date, ProcessExecutor::escape(trim($line))), $output, $path)) { - $newReference = trim($output); - } - - break; - } - } - - if (empty($newReference)) { - // no matching branch found, find the previous commit by date in all commits - if (0 !== $this->process->execute(sprintf($guessTemplate, $date, '--all'), $output, $path)) { - throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); - } - $newReference = trim($output); - } - - // checkout the new recovered ref - $command = sprintf($template, ProcessExecutor::escape($newReference)); - if (0 === $this->process->execute($command, $output, $path)) { - $this->io->write(' '.$reference.' is gone (history was rewritten?), recovered by checking out '.$newReference); - - return $newReference; - } + if (false !== strpos($this->process->getErrorOutput(), $reference)) { + $this->io->write(' '.$reference.' is gone (history was rewritten?)'); } throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput()); From 751190aafd14730ea4711fa35fced0f32f24dcaf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 17:21:48 +0100 Subject: [PATCH 445/638] Add new github-expose-hostname to docs/schema/config command, refs #3164 --- doc/04-schema.md | 3 +++ res/composer-schema.json | 4 ++++ src/Composer/Command/ConfigCommand.php | 1 + 3 files changed, 8 insertions(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index 2ebff5a30..c768edb72 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -790,6 +790,9 @@ The following options are supported: the autoloader. * **github-domains:** Defaults to `["github.com"]`. A list of domains to use in github mode. This is used for GitHub Enterprise setups. +* **github-expose-hostname:** Defaults to `true`. If set to false, the OAuth + tokens created to access the github API will have a date instead of the + machine hostname. * **notify-on-install:** Defaults to `true`. Composer allows repositories to define a notification URL, so that they get notified whenever a package from that repository is installed. This option allows you to disable that behaviour. diff --git a/res/composer-schema.json b/res/composer-schema.json index 9b1b31d42..95cd199e3 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -203,6 +203,10 @@ "items": { "type": "string" } + }, + "github-expose-hostname": { + "type": "boolean", + "description": "Defaults to true. If set to false, the OAuth tokens created to access the github API will have a date instead of the machine hostname." } } }, diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 67a2f70a8..ab3f1e8e8 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -323,6 +323,7 @@ EOT 'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }), 'optimize-autoloader' => array($booleanValidator, $booleanNormalizer), 'prepend-autoloader' => array($booleanValidator, $booleanNormalizer), + 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer), ); $multiConfigValues = array( 'github-protocols' => array( From 94926218e83b4578d30f385ee21bb1653e31bf83 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Oct 2014 18:46:01 +0100 Subject: [PATCH 446/638] CS fixes --- src/Composer/Autoload/AutoloadGenerator.php | 7 ++---- src/Composer/Cache.php | 4 +-- src/Composer/Command/AboutCommand.php | 1 - src/Composer/Command/ArchiveCommand.php | 1 - src/Composer/Command/ClearCacheCommand.php | 2 ++ src/Composer/Command/ConfigCommand.php | 4 +-- src/Composer/Command/CreateProjectCommand.php | 1 - src/Composer/Command/HomeCommand.php | 11 +++----- src/Composer/Command/InitCommand.php | 7 +++--- src/Composer/Command/InstallCommand.php | 2 +- src/Composer/Command/RemoveCommand.php | 2 +- src/Composer/Command/RequireCommand.php | 2 +- src/Composer/Command/UpdateCommand.php | 2 +- src/Composer/Config/JsonConfigSource.php | 2 +- src/Composer/Console/Application.php | 5 ++-- src/Composer/DependencyResolver/Pool.php | 2 +- src/Composer/DependencyResolver/Solver.php | 4 --- .../DependencyResolver/Transaction.php | 2 -- src/Composer/Downloader/ArchiveDownloader.php | 2 +- src/Composer/Downloader/FileDownloader.php | 2 +- .../Downloader/PearPackageExtractor.php | 21 ++++++++-------- src/Composer/EventDispatcher/Event.php | 6 ++--- .../EventDispatcher/EventDispatcher.php | 25 +++++++++---------- src/Composer/Factory.php | 12 ++++----- src/Composer/Installer.php | 6 ++--- src/Composer/Installer/PluginInstaller.php | 1 - src/Composer/Json/JsonFile.php | 4 +-- src/Composer/Json/JsonManipulator.php | 2 +- .../LinkConstraint/SpecificConstraint.php | 1 - src/Composer/Package/Loader/ArrayLoader.php | 2 +- src/Composer/Package/Locker.php | 2 +- .../Package/Version/VersionParser.php | 6 +++-- .../Package/Version/VersionSelector.php | 7 +++--- .../Repository/ArtifactRepository.php | 2 +- .../Pear/PackageDependencyParser.php | 4 +-- src/Composer/Repository/Vcs/GitDriver.php | 2 +- .../Repository/Vcs/PerforceDriver.php | 1 - src/Composer/Repository/Vcs/SvnDriver.php | 1 - src/Composer/Util/NoProxyPattern.php | 2 +- src/Composer/Util/Perforce.php | 1 - src/Composer/Util/ProcessExecutor.php | 8 +++--- src/Composer/Util/Svn.php | 1 - .../Test/Autoload/ClassLoaderTest.php | 2 +- .../Test/Config/JsonConfigSourceTest.php | 1 - .../Test/DependencyResolver/SolverTest.php | 3 +-- .../Downloader/PearPackageExtractorTest.php | 14 +++++------ .../Downloader/PerforceDownloaderTest.php | 1 - tests/Composer/Test/InstallerTest.php | 2 +- tests/Composer/Test/Json/JsonFileTest.php | 1 - .../Composer/Test/Json/JsonFormatterTest.php | 1 - .../Test/Mock/RemoteFilesystemMock.php | 1 - .../Package/Archiver/ArchiveManagerTest.php | 1 - .../Test/Plugin/PluginInstallerTest.php | 2 +- .../Repository/ArtifactRepositoryTest.php | 2 -- .../Repository/Vcs/PerforceDriverTest.php | 3 +-- tests/Composer/Test/Util/PerforceTest.php | 1 - 56 files changed, 94 insertions(+), 123 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 45c105ef9..19f194fa1 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -54,7 +54,7 @@ class AutoloadGenerator public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '') { $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array( - 'optimize' => (bool) $scanPsr0Packages + 'optimize' => (bool) $scanPsr0Packages, )); $filesystem = new Filesystem(); @@ -255,7 +255,7 @@ EOF; $packageMap[] = array( $package, - $installationManager->getInstallPath($package) + $installationManager->getInstallPath($package), ); } @@ -533,7 +533,6 @@ INCLUDEPATH; REGISTER_AUTOLOAD; - } $file .= <<root . $file, $contents); - } catch(\ErrorException $e) { + } catch (\ErrorException $e) { if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) { // Remove partial file. unlink($this->root . $file); @@ -150,7 +150,7 @@ class Cache public function gcIsNecessary() { - return (!self::$cacheCollected && !mt_rand(0, 50)); + return (!self::$cacheCollected && !mt_rand(0, 50)); } public function remove($file) diff --git a/src/Composer/Command/AboutCommand.php b/src/Composer/Command/AboutCommand.php index da0fb7fbe..ead7604df 100644 --- a/src/Composer/Command/AboutCommand.php +++ b/src/Composer/Command/AboutCommand.php @@ -40,6 +40,5 @@ EOT See http://getcomposer.org/ for more information. EOT ); - } } diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 34f6fe8a6..913a56a42 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -15,7 +15,6 @@ namespace Composer\Command; use Composer\Factory; use Composer\IO\IOInterface; use Composer\DependencyResolver\Pool; -use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\CompositeRepository; use Composer\Script\ScriptEvents; use Composer\Plugin\CommandEvent; diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index 47d139144..b1b9ecd9a 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -52,11 +52,13 @@ EOT $cachePath = realpath($cachePath); if (!$cachePath) { $io->write("Cache directory does not exist ($key): $cachePath"); + return; } $cache = new Cache($io, $cachePath); if (!$cache->isEnabled()) { $io->write("Cache is not enabled ($key): $cachePath"); + return; } diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index ab3f1e8e8..a08a6f4a6 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -160,7 +160,7 @@ EOT } $file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath(); - system($editor . ' ' . $file . (defined('PHP_WINDOWS_VERSION_BUILD') ? '': ' > `tty`')); + system($editor . ' ' . $file . (defined('PHP_WINDOWS_VERSION_BUILD') ? '' : ' > `tty`')); return 0; } @@ -359,7 +359,7 @@ EOT ); foreach ($uniqueConfigValues as $name => $callbacks) { - if ($settingKey === $name) { + if ($settingKey === $name) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 9928692eb..935370bfc 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -322,7 +322,6 @@ EOT return new InstallationManager(); } - /** * Updated preferSource or preferDist based on the preferredInstall config option * @param Config $config diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 023fb0843..5b0e32244 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -15,7 +15,6 @@ namespace Composer\Command; use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\Package\CompletePackageInterface; -use Composer\Package\Loader\InvalidPackageException; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryInterface; use Composer\Util\ProcessExecutor; @@ -23,7 +22,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Process\Exception\InvalidArgumentException; /** * @author Robert Schönthal @@ -92,8 +90,8 @@ EOT /** * finds a package by name * - * @param RepositoryInterface $repos - * @param string $name + * @param RepositoryInterface $repos + * @param string $name * @return CompletePackageInterface */ protected function getPackage(RepositoryInterface $repos, $name) @@ -142,8 +140,8 @@ EOT /** * initializes the repo * - * @param InputInterface $input - * @param OutputInterface $output + * @param InputInterface $input + * @param OutputInterface $output * @return CompositeRepository */ private function initializeRepo(InputInterface $input, OutputInterface $output) @@ -159,5 +157,4 @@ EOT return $repo; } - } diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 64e30de59..462b6d438 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -105,7 +105,7 @@ EOT } if (isset($options['require-dev'])) { - $options['require-dev'] = $this->formatRequirements($options['require-dev']) ; + $options['require-dev'] = $this->formatRequirements($options['require-dev']); if (array() === $options['require-dev']) { $options['require-dev'] = new \stdClass; } @@ -311,7 +311,6 @@ EOT foreach ($requires as $requirement) { if (!isset($requirement['version'])) { - // determine the best version automatically $version = $this->findBestVersionForPackage($input, $requirement['name']); $requirement['version'] = $version; @@ -553,8 +552,8 @@ EOT * * This returns a version with the ~ operator prefixed when possible. * - * @param InputInterface $input - * @param string $name + * @param InputInterface $input + * @param string $name * @return string * @throws \InvalidArgumentException */ diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 43a169e7d..669c03582 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -115,7 +115,7 @@ EOT ->setDevMode(!$input->getOption('no-dev')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index ee1754c65..cb83f56b7 100755 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -104,7 +104,7 @@ EOT ->setDevMode($updateDevMode) ->setUpdate(true) ->setUpdateWhitelist($packages) - ->setWhitelistDependencies($input->getOption('update-with-dependencies')); + ->setWhitelistDependencies($input->getOption('update-with-dependencies')) ; $status = $install->run(); diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 682a44e66..908159104 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -130,7 +130,7 @@ EOT ->setDevMode($updateDevMode) ->setUpdate(true) ->setUpdateWhitelist(array_keys($requirements)) - ->setWhitelistDependencies($input->getOption('update-with-dependencies')); + ->setWhitelistDependencies($input->getOption('update-with-dependencies')) ; $status = $install->run(); diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 9d1e95dd0..ebfd4cb2a 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -120,7 +120,7 @@ EOT ->setUpdate(true) ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 4d0a873f5..4fba2943c 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -187,7 +187,7 @@ class JsonConfigSource implements ConfigSourceInterface private function arrayUnshiftRef(&$array, &$value) { $return = array_unshift($array, ''); - $array[0] =& $value; + $array[0] = &$value; return $return; } diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 7148a9a11..6b4167ee7 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -25,7 +25,6 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\IO\ConsoleIO; use Composer\Json\JsonValidationException; -use Composer\Json\JsonFile; use Composer\Util\ErrorHandler; /** @@ -180,7 +179,8 @@ class Application extends BaseApplication $output->writeln('The disk hosting '.$dir.' is full, this may be the cause of the following exception'); } } - } catch (\Exception $e) {} + } catch (\Exception $e) { + } return parent::renderException($exception, $output); } @@ -206,7 +206,6 @@ class Application extends BaseApplication $message = $e->getMessage() . ':' . PHP_EOL . $errors; throw new JsonValidationException($message); } - } return $this->composer; diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index d0c660f82..05645c87a 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -232,7 +232,7 @@ class Pool * packages must match or null to return all * @param bool $mustMatchName Whether the name of returned packages * must match the given name - * @return PackageInterface[] A set of packages + * @return PackageInterface[] A set of packages */ public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false) { diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 2a0256e40..36b1d9862 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -83,7 +83,6 @@ class Solver $conflict = $this->decisions->decisionRule($literal); if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) { - $problem = new Problem($this->pool); $problem->addRule($rule); @@ -609,7 +608,6 @@ class Solver $installedPos = 0; while (true) { - if (1 === $level) { $conflictRule = $this->propagate($level); if (null !== $conflictRule) { @@ -658,7 +656,6 @@ class Solver } if ($noneSatisfied && count($decisionQueue)) { - $oLevel = $level; $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule); @@ -742,7 +739,6 @@ class Solver // minimization step if (count($this->branches)) { - $lastLiteral = null; $lastLevel = null; $lastBranchIndex = 0; diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index 214c502d1..b9569b3fa 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -55,7 +55,6 @@ class Transaction if ($literal > 0) { if (isset($installMeansUpdateMap[abs($literal)]) && !$package instanceof AliasPackage) { - $source = $installMeansUpdateMap[abs($literal)]; $updateMap[$package->getId()] = array( @@ -88,7 +87,6 @@ class Transaction 'package' => $package, 'reason' => $reason, ); - } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 9d78d7e2d..204c09154 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -137,7 +137,7 @@ abstract class ArchiveDownloader extends FileDownloader /** * Returns the folder content, excluding dotfiles * - * @param string $dir Directory + * @param string $dir Directory * @return \SplFileInfo[] */ private function getFolderContent($dir) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 82a256b7c..0c03f18ab 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -101,7 +101,7 @@ class FileDownloader implements DownloaderInterface } } } - + $this->io->write(''); } diff --git a/src/Composer/Downloader/PearPackageExtractor.php b/src/Composer/Downloader/PearPackageExtractor.php index 3ed3ed7be..ff0c90a9f 100644 --- a/src/Composer/Downloader/PearPackageExtractor.php +++ b/src/Composer/Downloader/PearPackageExtractor.php @@ -138,8 +138,9 @@ class PearPackageExtractor { /** @var $package \SimpleXmlElement */ $package = simplexml_load_file($this->combine($source, 'package.xml')); - if(false === $package) + if (false === $package) { throw new \RuntimeException('Package definition file is not valid.'); + } $packageSchemaVersion = $package['version']; if ('1.0' == $packageSchemaVersion) { @@ -203,16 +204,16 @@ class PearPackageExtractor /** @var $child \SimpleXMLElement */ if ($child->getName() == 'dir') { $dirSource = $this->combine($source, (string) $child['name']); - $dirTarget = $child['baseinstalldir'] ? : $target; - $dirRole = $child['role'] ? : $role; + $dirTarget = $child['baseinstalldir'] ?: $target; + $dirRole = $child['role'] ?: $role; $dirFiles = $this->buildSourceList10($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName); $result = array_merge($result, $dirFiles); } elseif ($child->getName() == 'file') { - $fileRole = (string) $child['role'] ? : $role; + $fileRole = (string) $child['role'] ?: $role; if (isset($targetRoles[$fileRole])) { - $fileName = (string) ($child['name'] ? : $child[0]); // $child[0] means text content + $fileName = (string) ($child['name'] ?: $child[0]); // $child[0] means text content $fileSource = $this->combine($source, $fileName); - $fileTarget = $this->combine((string) $child['baseinstalldir'] ? : $target, $fileName); + $fileTarget = $this->combine((string) $child['baseinstalldir'] ?: $target, $fileName); if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) { $fileTarget = $packageName . '/' . $fileTarget; } @@ -233,15 +234,15 @@ class PearPackageExtractor /** @var $child \SimpleXMLElement */ if ('dir' == $child->getName()) { $dirSource = $this->combine($source, $child['name']); - $dirTarget = $child['baseinstalldir'] ? : $target; - $dirRole = $child['role'] ? : $role; + $dirTarget = $child['baseinstalldir'] ?: $target; + $dirRole = $child['role'] ?: $role; $dirFiles = $this->buildSourceList20($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName); $result = array_merge($result, $dirFiles); } elseif ('file' == $child->getName()) { - $fileRole = (string) $child['role'] ? : $role; + $fileRole = (string) $child['role'] ?: $role; if (isset($targetRoles[$fileRole])) { $fileSource = $this->combine($source, (string) $child['name']); - $fileTarget = $this->combine((string) ($child['baseinstalldir'] ? : $target), (string) $child['name']); + $fileTarget = $this->combine((string) ($child['baseinstalldir'] ?: $target), (string) $child['name']); $fileTasks = array(); foreach ($child->children('http://pear.php.net/dtd/tasks-1.0') as $taskNode) { if ('replace' == $taskNode->getName()) { diff --git a/src/Composer/EventDispatcher/Event.php b/src/Composer/EventDispatcher/Event.php index b9ebeb029..38ae4f440 100644 --- a/src/Composer/EventDispatcher/Event.php +++ b/src/Composer/EventDispatcher/Event.php @@ -42,9 +42,9 @@ class Event /** * Constructor. * - * @param string $name The event name - * @param array $args Arguments passed by the user - * @param array $flags Optional flags to pass data not as argument + * @param string $name The event name + * @param array $args Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument */ public function __construct($name, array $args = array(), array $flags = array()) { diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 3026d3c9c..0f534b470 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -79,12 +79,12 @@ class EventDispatcher /** * Dispatch a script event. * - * @param string $eventName The constant in ScriptEvents - * @param bool $devMode - * @param array $additionalArgs Arguments passed by the user - * @param array $flags Optional flags to pass data not as argument - * @return int return code of the executed script if any, for php scripts a false return - * value is changed to 1, anything else to 0 + * @param string $eventName The constant in ScriptEvents + * @param bool $devMode + * @param array $additionalArgs Arguments passed by the user + * @param array $flags Optional flags to pass data not as argument + * @return int return code of the executed script if any, for php scripts a false return + * value is changed to 1, anything else to 0 */ public function dispatchScript($eventName, $devMode = false, $additionalArgs = array(), $flags = array()) { @@ -108,19 +108,18 @@ class EventDispatcher /** * Dispatch a command event. * - * @param string $eventName The constant in ScriptEvents - * @param boolean $devMode Whether or not we are in dev mode + * @param string $eventName The constant in ScriptEvents + * @param boolean $devMode Whether or not we are in dev mode * @param array $additionalArgs Arguments passed by the user * @param array $flags Optional flags to pass data not as argument * @return int return code of the executed script if any, for php scripts a false return - * value is changed to 1, anything else to 0 + * value is changed to 1, anything else to 0 */ public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array(), $flags = array()) { return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags)); } - /** * Dispatch a installer event. * @@ -132,7 +131,7 @@ class EventDispatcher * @param array $operations The list of operations * * @return int return code of the executed script if any, for php scripts a false return - * value is changed to 1, anything else to 0 + * value is changed to 1, anything else to 0 */ public function dispatchInstallerEvent($eventName, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) { @@ -142,10 +141,10 @@ class EventDispatcher /** * Triggers the listeners of an event. * - * @param Event $event The event object to pass to the event handlers/listeners. + * @param Event $event The event object to pass to the event handlers/listeners. * @param string $additionalArgs * @return int return code of the executed script if any, for php scripts a false return - * value is changed to 1, anything else to 0 + * value is changed to 1, anything else to 0 * @throws \RuntimeException * @throws \Exception */ diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e495f05f6..4f7bfefd8 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -85,7 +85,7 @@ class Factory } /** - * @param IOInterface|null $io + * @param IOInterface|null $io * @return Config */ public static function createConfig(IOInterface $io = null) @@ -335,8 +335,8 @@ class Factory } /** - * @param Config $config - * @param string $vendorDir + * @param Config $config + * @param string $vendorDir * @return Repository\InstalledFilesystemRepository|null */ protected function createGlobalRepository(Config $config, $vendorDir) @@ -415,9 +415,9 @@ class Factory } /** - * @param Composer $composer - * @param IOInterface $io - * @param RepositoryInterface $globalRepository + * @param Composer $composer + * @param IOInterface $io + * @param RepositoryInterface $globalRepository * @return Plugin\PluginManager */ protected function createPluginManager(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 4fb58e07f..294ab0a0a 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -380,7 +380,7 @@ class Installer } if ($this->update) { - $this->io->write('Updating dependencies'.($withDevReqs?' (including require-dev)':'').''); + $this->io->write('Updating dependencies'.($withDevReqs ? ' (including require-dev)' : '').''); $request->updateAll(); @@ -431,7 +431,7 @@ class Installer } } } elseif ($installFromLock) { - $this->io->write('Installing dependencies'.($withDevReqs?' (including require-dev)':'').' from lock file'); + $this->io->write('Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').' from lock file'); if (!$this->locker->isFresh()) { $this->io->write('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.'); @@ -451,7 +451,7 @@ class Installer $request->install($link->getTarget(), $link->getConstraint()); } } else { - $this->io->write('Installing dependencies'.($withDevReqs?' (including require-dev)':'').''); + $this->io->write('Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').''); if ($withDevReqs) { $links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); diff --git a/src/Composer/Installer/PluginInstaller.php b/src/Composer/Installer/PluginInstaller.php index 61c5a2823..b5469ccc6 100644 --- a/src/Composer/Installer/PluginInstaller.php +++ b/src/Composer/Installer/PluginInstaller.php @@ -40,7 +40,6 @@ class PluginInstaller extends LibraryInstaller { parent::__construct($io, $composer, 'composer-plugin'); $this->installationManager = $composer->getInstallationManager(); - } /** diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 80718348c..bed35d598 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -188,8 +188,8 @@ class JsonFile // compact brackets to follow recent php versions if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) { - $json = preg_replace('/\[\s+\]/', '[]', $json); - $json = preg_replace('/\{\s+\}/', '{}', $json); + $json = preg_replace('/\[\s+\]/', '[]', $json); + $json = preg_replace('/\{\s+\}/', '{}', $json); } return $json; diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 6dccc62dc..dd8c49ebd 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -42,7 +42,7 @@ class JsonManipulator if (!$this->pregMatch('#^\{(.*)\}$#s', $contents)) { throw new \InvalidArgumentException('The json file must be an object ({})'); } - $this->newline = false !== strpos($contents, "\r\n") ? "\r\n": "\n"; + $this->newline = false !== strpos($contents, "\r\n") ? "\r\n" : "\n"; $this->contents = $contents === '{}' ? '{' . $this->newline . '}' : $contents; $this->detectIndenting(); } diff --git a/src/Composer/Package/LinkConstraint/SpecificConstraint.php b/src/Composer/Package/LinkConstraint/SpecificConstraint.php index eda09c6ec..e0904dbbf 100644 --- a/src/Composer/Package/LinkConstraint/SpecificConstraint.php +++ b/src/Composer/Package/LinkConstraint/SpecificConstraint.php @@ -50,5 +50,4 @@ abstract class SpecificConstraint implements LinkConstraintInterface // implementations must implement a method of this format: // not declared abstract here because type hinting violates parameter coherence (TODO right word?) // public function matchSpecific( $provider); - } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 142b15c72..d25ea5546 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -67,7 +67,7 @@ class ArrayLoader implements LoaderInterface throw new \UnexpectedValueException('Package '.$config['name'].'\'s bin key should be an array, '.gettype($config['bin']).' given.'); } foreach ($config['bin'] as $key => $bin) { - $config['bin'][$key]= ltrim($bin, '/'); + $config['bin'][$key] = ltrim($bin, '/'); } $package->setBinaries($config['bin']); } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 4eb836706..9d49f6eaf 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -297,7 +297,7 @@ class Locker $time = $this->getPackageTime($package) ?: $time; } if (null !== $time) { - $spec['time'] = $time; + $spec['time'] = $time; } unset($spec['installation-source']); diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 3825426a8..f14f09622 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -150,7 +150,8 @@ class VersionParser if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) { try { return $this->normalizeBranch($match[1]); - } catch (\Exception $e) {} + } catch (\Exception $e) { + } } $extraMessage = ''; @@ -365,7 +366,8 @@ class VersionParser } return array(new VersionConstraint($matches[1] ?: '=', $version)); - } catch (\Exception $e) { } + } catch (\Exception $e) { + } } $message = 'Could not parse version constraint '.$constraint; diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 29358027f..202b11883 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -37,8 +37,8 @@ class VersionSelector * Given a package name and optional version, returns the latest PackageInterface * that matches. * - * @param string $packageName - * @param string $targetPackageVersion + * @param string $packageName + * @param string $targetPackageVersion * @return PackageInterface|bool */ public function findBestCandidate($packageName, $targetPackageVersion = null) @@ -73,7 +73,7 @@ class VersionSelector * * dev-master -> ~2.1@dev (dev version with alias) * * dev-master -> dev-master (dev versions are untouched) * - * @param PackageInterface $package + * @param PackageInterface $package * @return string */ public function findRecommendedRequireVersion(PackageInterface $package) @@ -90,6 +90,7 @@ class VersionSelector $extra = preg_replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count); if ($count) { $extra = str_replace('.9999999', '.0', $extra); + return $this->transformVersion($extra, $extra, 'dev'); } } diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 0b7775aaf..38936e40a 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -96,7 +96,7 @@ class ArtifactRepository extends ArrayRepository return $i; } - if(strpos($directoryName, '\\') !== false || + if (strpos($directoryName, '\\') !== false || strpos($directoryName, '/') !== false) { //composer.json files below first directory are rejected continue; diff --git a/src/Composer/Repository/Pear/PackageDependencyParser.php b/src/Composer/Repository/Pear/PackageDependencyParser.php index 515993d03..a124cd07a 100644 --- a/src/Composer/Repository/Pear/PackageDependencyParser.php +++ b/src/Composer/Repository/Pear/PackageDependencyParser.php @@ -51,7 +51,7 @@ class PackageDependencyParser */ private function buildDependency10Info($depArray) { - static $dep10toOperatorMap = array('has'=>'==', 'eq' => '==', 'ge' => '>=', 'gt' => '>', 'le' => '<=', 'lt' => '<', 'not' => '!='); + static $dep10toOperatorMap = array('has' => '==', 'eq' => '==', 'ge' => '>=', 'gt' => '>', 'le' => '<=', 'lt' => '<', 'not' => '!='); $result = array(); @@ -255,7 +255,7 @@ class PackageDependencyParser */ private function parse20VersionConstraint(array $data) { - static $dep20toOperatorMap = array('has'=>'==', 'min' => '>=', 'max' => '<=', 'exclude' => '!='); + static $dep20toOperatorMap = array('has' => '==', 'min' => '>=', 'max' => '<=', 'exclude' => '!='); $versions = array(); $values = array_intersect_key($data, $dep20toOperatorMap); diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index a020b92c2..a47643988 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -203,7 +203,7 @@ class GitDriver extends VcsDriver foreach ($this->process->splitLines($output) as $branch) { if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) { if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) { - $branches[$match[1]] = $match[2]; + $branches[$match[1]] = $match[2]; } } } diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 7e313bf63..c7c225b27 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -182,5 +182,4 @@ class PerforceDriver extends VcsDriver { return $this->branch; } - } diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 729bda94e..c8d9a17a6 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -27,7 +27,6 @@ use Composer\Downloader\TransportException; */ class SvnDriver extends VcsDriver { - /** * @var Cache */ diff --git a/src/Composer/Util/NoProxyPattern.php b/src/Composer/Util/NoProxyPattern.php index 533dbc19c..4e5c0c76b 100644 --- a/src/Composer/Util/NoProxyPattern.php +++ b/src/Composer/Util/NoProxyPattern.php @@ -126,7 +126,7 @@ class NoProxyPattern // Now do some bit shifting/switching to convert to ints $i = ($a << 24) + ($b << 16) + ($c << 8) + $d; - $mask = $bits == 0 ? 0: (~0 << (32 - $bits)); + $mask = $bits == 0 ? 0 : (~0 << (32 - $bits)); // Here's our lowest int $low = $i & $mask; diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index d3dcd696f..8e79f87b3 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -570,5 +570,4 @@ class Perforce { $this->filesystem = $fs; } - } diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index d5a0549d3..46dafb0ae 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -105,7 +105,7 @@ class ProcessExecutor { static::$timeout = $timeout; } - + /** * Escapes a string to be used as a shell argument. * @@ -113,9 +113,9 @@ class ProcessExecutor * * @return string The escaped argument */ - - public static function escape ($argument) + + public static function escape($argument) { - return ProcessUtils::escapeArgument($argument); + return ProcessUtils::escapeArgument($argument); } } diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 4b6d615fb..4949aa271 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -137,7 +137,6 @@ class Svn // try to authenticate if maximum quantity of tries not reached if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES) { - // restart the process return $this->execute($command, $url, $cwd, $path, $verbose); } diff --git a/tests/Composer/Test/Autoload/ClassLoaderTest.php b/tests/Composer/Test/Autoload/ClassLoaderTest.php index eee942e42..ad42e233a 100644 --- a/tests/Composer/Test/Autoload/ClassLoaderTest.php +++ b/tests/Composer/Test/Autoload/ClassLoaderTest.php @@ -18,7 +18,7 @@ class ClassLoaderTest extends \PHPUnit_Framework_TestCase * @param bool $prependSeparator Whether to call ->loadClass() with a class name with preceding * namespace separator, as it happens in PHP 5.3.0 - 5.3.2. See https://bugs.php.net/50731 */ - public function testLoadClass($class, $prependSeparator = FALSE) + public function testLoadClass($class, $prependSeparator = false) { $loader = new ClassLoader(); $loader->add('Namespaced\\', __DIR__ . '/Fixtures'); diff --git a/tests/Composer/Test/Config/JsonConfigSourceTest.php b/tests/Composer/Test/Config/JsonConfigSourceTest.php index d0b78f3e3..b7a3b592c 100644 --- a/tests/Composer/Test/Config/JsonConfigSourceTest.php +++ b/tests/Composer/Test/Config/JsonConfigSourceTest.php @@ -48,7 +48,6 @@ class JsonConfigSourceTest extends \PHPUnit_Framework_TestCase $value, $this->fixturePath('addLink/'.$fixtureBasename.'.json'), ); - } /** diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index ac78b5a26..d3b540f47 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -654,8 +654,7 @@ class SolverTest extends TestCase public function testConflictResultEmpty() { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); - $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));; - + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $packageA->setConflicts(array( 'b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'conflicts'), )); diff --git a/tests/Composer/Test/Downloader/PearPackageExtractorTest.php b/tests/Composer/Test/Downloader/PearPackageExtractorTest.php index fa393833d..c053976d7 100644 --- a/tests/Composer/Test/Downloader/PearPackageExtractorTest.php +++ b/tests/Composer/Test/Downloader/PearPackageExtractorTest.php @@ -25,19 +25,19 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase $fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v1.0', array('php' => '/'), array()); $expectedFileActions = array( - 'Gtk.php' => Array( + 'Gtk.php' => array( 'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk.php', 'to' => 'PEAR/Frontend/Gtk.php', 'role' => 'php', 'tasks' => array(), ), - 'Gtk/Config.php' => Array( + 'Gtk/Config.php' => array( 'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk/Config.php', 'to' => 'PEAR/Frontend/Gtk/Config.php', 'role' => 'php', 'tasks' => array(), ), - 'Gtk/xpm/black_close_icon.xpm' => Array( + 'Gtk/xpm/black_close_icon.xpm' => array( 'from' => 'PEAR_Frontend_Gtk-0.4.0/Gtk/xpm/black_close_icon.xpm', 'to' => 'PEAR/Frontend/Gtk/xpm/black_close_icon.xpm', 'role' => 'php', @@ -56,7 +56,7 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase $fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.0', array('php' => '/'), array()); $expectedFileActions = array( - 'URL.php' => Array( + 'URL.php' => array( 'from' => 'Net_URL-1.0.15/URL.php', 'to' => 'Net/URL.php', 'role' => 'php', @@ -75,13 +75,13 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase $fileActions = $method->invoke($extractor, __DIR__ . '/Fixtures/Package_v2.1', array('php' => '/', 'script' => '/bin'), array()); $expectedFileActions = array( - 'php/Zend/Authentication/Storage/StorageInterface.php' => Array( + 'php/Zend/Authentication/Storage/StorageInterface.php' => array( 'from' => 'Zend_Authentication-2.0.0beta4/php/Zend/Authentication/Storage/StorageInterface.php', 'to' => '/php/Zend/Authentication/Storage/StorageInterface.php', 'role' => 'php', 'tasks' => array(), ), - 'php/Zend/Authentication/Result.php' => Array( + 'php/Zend/Authentication/Result.php' => array( 'from' => 'Zend_Authentication-2.0.0beta4/php/Zend/Authentication/Result.php', 'to' => '/php/Zend/Authentication/Result.php', 'role' => 'php', @@ -98,7 +98,7 @@ class PearPackageExtractorTest extends \PHPUnit_Framework_TestCase ) ) ), - 'renamedFile.php' => Array( + 'renamedFile.php' => array( 'from' => 'Zend_Authentication-2.0.0beta4/renamedFile.php', 'to' => 'correctFile.php', 'role' => 'php', diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php index 9e26689a9..3e2bce2b4 100644 --- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -22,7 +22,6 @@ use Composer\IO\IOInterface; */ class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase { - protected $config; protected $downloader; protected $io; diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index cc12cb3ef..03cc10628 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -152,7 +152,7 @@ class InstallerTest extends TestCase $io->expects($this->any()) ->method('write') ->will($this->returnCallback(function ($text, $newline) use (&$output) { - $output .= $text . ($newline ? "\n":""); + $output .= $text . ($newline ? "\n" : ""); })); $composer = FactoryMock::create($io, $composerConfig); diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index cf89da35e..9036a23e8 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -226,5 +226,4 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase $this->assertEquals($json, $file->encode($data, $options)); } } - } diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php index eb6e87b00..557312fc3 100644 --- a/tests/Composer/Test/Json/JsonFormatterTest.php +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -46,5 +46,4 @@ class JsonFormatterTest extends \PHPUnit_Framework_TestCase return implode('+', $codes); } - } diff --git a/tests/Composer/Test/Mock/RemoteFilesystemMock.php b/tests/Composer/Test/Mock/RemoteFilesystemMock.php index caf1c5e65..79a25e550 100644 --- a/tests/Composer/Test/Mock/RemoteFilesystemMock.php +++ b/tests/Composer/Test/Mock/RemoteFilesystemMock.php @@ -36,5 +36,4 @@ class RemoteFilesystemMock extends RemoteFilesystem throw new TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404); } - } diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 5a9f8c2d8..aa98a62a2 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -13,7 +13,6 @@ namespace Composer\Test\Package\Archiver; use Composer\Factory; -use Composer\Package\Archiver; use Composer\Package\PackageInterface; class ArchiveManagerTest extends ArchiverTest diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index bd9e2b554..1799eb2b8 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -39,7 +39,7 @@ class PluginInstallerTest extends \PHPUnit_Framework_TestCase $this->directory = sys_get_temp_dir() . '/' . uniqid(); for ($i = 1; $i <= 4; $i++) { $filename = '/Fixtures/plugin-v'.$i.'/composer.json'; - mkdir(dirname($this->directory . $filename), 0777, TRUE); + mkdir(dirname($this->directory . $filename), 0777, true); $this->packages[] = $loader->load(__DIR__ . $filename); } diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index db7ca42ba..7d64a0141 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -16,7 +16,6 @@ use Composer\TestCase; use Composer\IO\NullIO; use Composer\Config; use Composer\Package\BasePackage; -use Composer\Util\Filesystem; class ArtifactRepositoryTest extends TestCase { @@ -67,7 +66,6 @@ class ArtifactRepositoryTest extends TestCase $this->assertTrue(strpos($package->getDistUrl(), $relativePath) === 0); } } - } //Files jsonInFirstLevel.zip, jsonInRoot.zip and jsonInSecondLevel.zip were generated with: diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 09762f26e..59030f506 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -73,7 +73,7 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase protected function getTestConfig($testPath) { $config = new Config(); - $config->merge(array('config'=>array('home'=>$testPath))); + $config->merge(array('config' => array('home' => $testPath))); return $config; } @@ -173,5 +173,4 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->perforce->expects($this->once())->method('cleanupClientSpec'); $this->driver->cleanup(); } - } diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php index 91575b06d..dc727096d 100644 --- a/tests/Composer/Test/Util/PerforceTest.php +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -718,5 +718,4 @@ class PerforceTest extends \PHPUnit_Framework_TestCase $this->perforce->cleanupClientSpec(); } - } From 5a473439ed28bdf7ce397d607fa99cb2df9d80c3 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Fri, 17 Oct 2014 15:07:26 -0300 Subject: [PATCH 447/638] Updated $branchAliasVersion set based on @Seldaek's suggestion. --- src/Composer/Compiler.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 20989cf94..b1b3725c6 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -50,13 +50,7 @@ class Compiler if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); } - $localConfig = __DIR__.'/../../composer.json'; - $file = new JsonFile($localConfig); - $localConfig = $file->read(); - if (isset($localConfig['extra']['branch-alias']['dev-master'])) { - $this->branchAliasVersion = $localConfig['extra']['branch-alias']['dev-master']; - } $date = new \DateTime(trim($process->getOutput())); $date->setTimezone(new \DateTimeZone('UTC')); $this->versionDate = $date->format('Y-m-d H:i:s'); @@ -64,6 +58,14 @@ class Compiler $process = new Process('git describe --tags HEAD'); if ($process->run() == 0) { $this->version = trim($process->getOutput()); + } else { + // get branch-alias defined in composer.json for dev-master (if any) + $localConfig = __DIR__.'/../../composer.json'; + $file = new JsonFile($localConfig); + $localConfig = $file->read(); + if (isset($localConfig['extra']['branch-alias']['dev-master'])) { + $this->branchAliasVersion = $localConfig['extra']['branch-alias']['dev-master']; + } } $phar = new \Phar($pharFile, 0, 'composer.phar'); From fb1747624c08c74366d34f054d44abd7f0191065 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Oct 2014 19:50:22 +0100 Subject: [PATCH 448/638] Force the origin remote url to be the original one and not that of a mirror --- src/Composer/Downloader/GitDownloader.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 503d792a1..a0e2a9adf 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -60,6 +60,10 @@ class GitDownloader extends VcsDownloader } $package->setSourceReference($newRef); } + + if ($url !== $package->getSourceUrl()) { + $this->process->execute(sprintf('git remote set-url origin %s', $package->getSourceUrl()), $output, $path); + } } /** From a309e1d89ded6919935a842faeaed8e888fbfe37 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Oct 2014 20:16:14 +0100 Subject: [PATCH 449/638] Fix tests and run it before the push url updater --- src/Composer/Downloader/GitDownloader.php | 8 +++--- .../Test/Downloader/GitDownloaderTest.php | 25 +++++++++++++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index a0e2a9adf..bdebaf4cf 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -52,6 +52,10 @@ class GitDownloader extends VcsDownloader }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); + if ($url !== $package->getSourceUrl()) { + $url = $package->getSourceUrl(); + $this->process->execute(sprintf('git remote set-url origin %s', ProcessExecutor::escape($url)), $output, $path); + } $this->setPushUrl($path, $url); if ($newRef = $this->updateToCommit($path, $ref, $package->getPrettyVersion(), $package->getReleaseDate())) { @@ -60,10 +64,6 @@ class GitDownloader extends VcsDownloader } $package->setSourceReference($newRef); } - - if ($url !== $package->getSourceUrl()) { - $this->process->execute(sprintf('git remote set-url origin %s', $package->getSourceUrl()), $output, $path); - } } /** diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 194fc96c9..05cc8f30e 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -53,6 +53,9 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase $packageMock->expects($this->any()) ->method('getSourceUrls') ->will($this->returnValue(array('https://example.com/composer/composer'))); + $packageMock->expects($this->any()) + ->method('getSourceUrl') + ->will($this->returnValue('https://example.com/composer/composer')); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('dev-master')); @@ -91,36 +94,45 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('ref')); $packageMock->expects($this->any()) ->method('getSourceUrls') - ->will($this->returnValue(array('https://github.com/composer/composer'))); + ->will($this->returnValue(array('https://github.com/mirrors/composer', 'https://github.com/composer/composer'))); + $packageMock->expects($this->any()) + ->method('getSourceUrl') + ->will($this->returnValue('https://github.com/composer/composer')); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('1.0.0')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git://github.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git://github.com/mirrors/composer' && git fetch composer"); $processExecutor->expects($this->at(0)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(1)); - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/mirrors/composer' && git fetch composer"); $processExecutor->expects($this->at(2)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(0)); - $expectedGitCommand = $this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'"); + $expectedGitCommand = $this->winCompat("git remote set-url origin 'https://github.com/composer/composer'"); $processExecutor->expects($this->at(3)) ->method('execute') ->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); + $expectedGitCommand = $this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'"); $processExecutor->expects($this->at(4)) + ->method('execute') + ->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) + ->will($this->returnValue(0)); + + $processExecutor->expects($this->at(5)) ->method('execute') ->with($this->equalTo('git branch -r')) ->will($this->returnValue(0)); - $processExecutor->expects($this->at(5)) + $processExecutor->expects($this->at(6)) ->method('execute') ->with($this->equalTo($this->winCompat("git checkout 'ref' && git reset --hard 'ref'")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); @@ -149,6 +161,9 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase $packageMock->expects($this->any()) ->method('getSourceUrls') ->will($this->returnValue(array('https://github.com/composer/composer'))); + $packageMock->expects($this->any()) + ->method('getSourceUrl') + ->will($this->returnValue('https://github.com/composer/composer')); $packageMock->expects($this->any()) ->method('getPrettyVersion') ->will($this->returnValue('1.0.0')); From 2a4b12512556e5d02201117a4debf9faedc8c88b Mon Sep 17 00:00:00 2001 From: mpoiriert Date: Thu, 30 Oct 2014 09:19:10 -0400 Subject: [PATCH 450/638] change preProcess from private to public I need to override the preProcess method from a child class. The use case is related to package renaming in a private satis repository. I understand the implication for packagist (and other public repository) but keeping the root packageName cause problem when you need to rename a package. I will override override the name assignation with this ```PHP // use the main identifier if name is not present $data['name'] = !isset($data['name']) ? $this->packageName : $data['name']; ``` --- src/Composer/Repository/VcsRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 62e7acef5..6c9dc5d4b 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -265,7 +265,7 @@ class VcsRepository extends ArrayRepository } } - private function preProcess(VcsDriverInterface $driver, array $data, $identifier) + protected function preProcess(VcsDriverInterface $driver, array $data, $identifier) { // keep the name of the main identifier for all packages $data['name'] = $this->packageName ?: $data['name']; From 9e0a85fb640cad7522333cfb3445a09ac105cc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20R?= Date: Sun, 2 Nov 2014 20:44:04 +0100 Subject: [PATCH 451/638] Fix use of non existing ->getIO() function --- src/Composer/EventDispatcher/EventDispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 0f534b470..8d5ee9fa1 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -179,7 +179,7 @@ class EventDispatcher } else { $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments())); if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { - $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); + $this->io->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode); } From 7f64afcbe86dbc97f426eb86533aee6c5e086e71 Mon Sep 17 00:00:00 2001 From: Veres Lajos Date: Tue, 4 Nov 2014 21:47:59 +0000 Subject: [PATCH 452/638] typo fix --- doc/03-cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 23fdbebd8..215340fc4 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -316,7 +316,7 @@ php composer.phar validate ### Options -* **--no-check-all:** Wether or not composer do a complete validation. +* **--no-check-all:** Whether or not composer do a complete validation. ## status From cfa8432a603bdd4c0ab650b2f4bce3f11a988039 Mon Sep 17 00:00:00 2001 From: d060631 Date: Fri, 31 Oct 2014 09:13:13 +0100 Subject: [PATCH 453/638] add no-dev option to licenses command --- src/Composer/Command/LicensesCommand.php | 59 +++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index 5d05ef74f..321d17709 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -16,6 +16,8 @@ use Composer\Json\JsonFile; use Composer\Package\Version\VersionParser; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Package\PackageInterface; +use Composer\Repository\RepositoryInterface; use Symfony\Component\Console\Helper\TableHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -26,6 +28,12 @@ use Symfony\Component\Console\Output\OutputInterface; */ class LicensesCommand extends Command { + /** + * @var list of packages to use + */ + + private $packageList = array(); + protected function configure() { $this @@ -33,6 +41,7 @@ class LicensesCommand extends Command ->setDescription('Show information about licenses of dependencies') ->setDefinition(array( new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp(<<getPackages() as $package) { - $packages[$package->getName()] = $package; + if ($input->getOption('no-dev')) { + $this->findRequiresPackages($repo, $root); + } else { + $this->addToPackageList($repo->getPackages()); } - ksort($packages); + ksort($this->packageList); switch ($format = $input->getOption('format')) { case 'text': @@ -72,7 +82,7 @@ EOT $table = $this->getHelperSet()->get('table'); $table->setLayout(TableHelper::LAYOUT_BORDERLESS); $table->setHorizontalBorderChar(''); - foreach ($packages as $package) { + foreach ($this->packageList as $package) { $table->addRow(array( $package->getPrettyName(), $versionParser->formatVersion($package), @@ -83,7 +93,7 @@ EOT break; case 'json': - foreach ($packages as $package) { + foreach ($this->packageList as $package) { $dependencies[$package->getPrettyName()] = array( 'version' => $versionParser->formatVersion($package), 'license' => $package->getLicense(), @@ -102,4 +112,41 @@ EOT throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format)); } } + + /** + * Find package requires and child requires + * + * @param RepositoryInterface $repo + * @param PackageInterface $package + */ + private function findRequiresPackages(RepositoryInterface $repo, PackageInterface $package) + { + $requires = array_keys($package->getRequires()); + + $packageListNames = array_keys($this->packageList); + $packages = array_filter( + $repo->getPackages(), + function ($package) use ($requires, $packageListNames) { + return in_array($package->getName(), $requires) && !in_array($package->getName(), $packageListNames); + } + ); + + $this->addToPackageList($packages); + + foreach ($packages as $package) { + $this->findRequiresPackages($repo, $package); + } + } + + /** + * Adds packages to the package list + * + * @param array $packages the list of packages to add + */ + public function addToPackageList($packages) + { + foreach ($packages as $package) { + $this->packageList[$package->getName()] = $package; + } + } } From 6784570691bdfd88ecf71a0983ca0e215535fdb3 Mon Sep 17 00:00:00 2001 From: Olivier Laurendeau Date: Fri, 14 Nov 2014 17:32:31 +0100 Subject: [PATCH 454/638] Add ignore-platform-reqs option to remove command --- src/Composer/Command/RemoveCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index cb83f56b7..c292a2812 100755 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -41,6 +41,7 @@ class RemoveCommand extends Command new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), )) ->setHelp(<<remove command removes a package from the current @@ -105,6 +106,7 @@ EOT ->setUpdate(true) ->setUpdateWhitelist($packages) ->setWhitelistDependencies($input->getOption('update-with-dependencies')) + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ; $status = $install->run(); From b6b3cf3a49e99d1c4b0643a1cb79ec27d13c445d Mon Sep 17 00:00:00 2001 From: Olivier Laurendeau Date: Fri, 14 Nov 2014 17:32:42 +0100 Subject: [PATCH 455/638] Add ignore-platform-reqs option to require command --- src/Composer/Command/RequireCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 908159104..d0a63f1f3 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -44,6 +44,7 @@ class RequireCommand extends InitCommand new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), )) ->setHelp(<<setUpdate(true) ->setUpdateWhitelist(array_keys($requirements)) ->setWhitelistDependencies($input->getOption('update-with-dependencies')) + ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ; $status = $install->run(); From af79a104d914acd7e2b3367de422bfb8e378e2ea Mon Sep 17 00:00:00 2001 From: Olivier Laurendeau Date: Sat, 15 Nov 2014 10:35:21 +0100 Subject: [PATCH 456/638] Update require command's documentation --- doc/03-cli.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 215340fc4..f4c1c37e0 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -165,6 +165,9 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master * **--prefer-source:** Install packages from `source` when available. * **--prefer-dist:** Install packages from `dist` when available. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and force the installation even if the local machine does not + fulfill these. * **--dev:** Add packages to `require-dev`. * **--no-update:** Disables the automatic update of the dependencies. * **--no-progress:** Removes the progress display that can mess with some From 1c5c334b48894a85512624523c0b6d2055f3fb5a Mon Sep 17 00:00:00 2001 From: Olivier Laurendeau Date: Sat, 15 Nov 2014 10:36:15 +0100 Subject: [PATCH 457/638] Update remove command's documentation --- doc/03-cli.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index f4c1c37e0..7fcafc2d6 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -189,6 +189,9 @@ After removing the requirements, the modified requirements will be uninstalled. ### Options +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and force the installation even if the local machine does not + fulfill these. * **--dev:** Remove packages from `require-dev`. * **--no-update:** Disables the automatic update of the dependencies. * **--no-progress:** Removes the progress display that can mess with some From 116ccdac627080916c1d9d168aa8e94aa26c4898 Mon Sep 17 00:00:00 2001 From: kaktus42 Date: Sun, 16 Nov 2014 23:34:25 +0100 Subject: [PATCH 458/638] return false if local file path does not exist in GitDriver and HgDriver --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- src/Composer/Repository/Vcs/HgDriver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index a47643988..ecb92a50b 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -227,7 +227,7 @@ class GitDriver extends VcsDriver if (Filesystem::isLocalPath($url)) { $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { - throw new \RuntimeException('Directory does not exist: '.$url); + return false; } $process = new ProcessExecutor($io); diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index eb962f276..5fd1ef06f 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -200,7 +200,7 @@ class HgDriver extends VcsDriver if (Filesystem::isLocalPath($url)) { $url = Filesystem::getPlatformPath($url); if (!is_dir($url)) { - throw new \RuntimeException('Directory does not exist: '.$url); + return false; } $process = new ProcessExecutor(); From a531594a4adf938dc0063650c24d005760b86f29 Mon Sep 17 00:00:00 2001 From: Michael Gusev Date: Sun, 16 Nov 2014 14:24:44 +0100 Subject: [PATCH 459/638] Issue #3422 https://github.com/composer/composer/issues/3422 --- src/Composer/Autoload/ClassLoader.php | 5 ++++- tests/Composer/Test/Autoload/ClassLoaderTest.php | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 443364959..4a56396af 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -56,7 +56,10 @@ class ClassLoader public function getPrefixes() { - return call_user_func_array('array_merge', $this->prefixesPsr0); + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + return array(); } public function getPrefixesPsr4() diff --git a/tests/Composer/Test/Autoload/ClassLoaderTest.php b/tests/Composer/Test/Autoload/ClassLoaderTest.php index ad42e233a..50772101e 100644 --- a/tests/Composer/Test/Autoload/ClassLoaderTest.php +++ b/tests/Composer/Test/Autoload/ClassLoaderTest.php @@ -55,4 +55,13 @@ class ClassLoaderTest extends \PHPUnit_Framework_TestCase array('ShinyVendor\\ShinyPackage\\SubNamespace\\Bar', true), ); } + + /** + * getPrefixes method should return empty array if ClassLoader does not have any psr-0 configuration + */ + public function testGetPrefixesWithNoPSR0Configuration() + { + $loader = new ClassLoader(); + $this->assertEmpty($loader->getPrefixes()); + } } From f3d8323a719dd3e734da67a72aca2425b8d0755c Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 18 Nov 2014 11:18:14 +0100 Subject: [PATCH 460/638] Fixed the json formatting when using JSONC --- src/Composer/Json/JsonFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index bed35d598..e008ced9d 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -187,7 +187,7 @@ class JsonFile $json = json_encode($data, $options); // compact brackets to follow recent php versions - if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) { + if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || defined('JSON_C_VERSION')) { $json = preg_replace('/\[\s+\]/', '[]', $json); $json = preg_replace('/\{\s+\}/', '{}', $json); } From 494f5698c4ea7f651c0b82b9377300ffd1546c32 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 18 Nov 2014 10:28:44 +0000 Subject: [PATCH 461/638] Update SPDX licenses, fixes #3431 --- res/spdx-identifier.json | 85 +++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/res/spdx-identifier.json b/res/spdx-identifier.json index b90531711..b6d8dbc1f 100644 --- a/res/spdx-identifier.json +++ b/res/spdx-identifier.json @@ -1,44 +1,59 @@ [ - "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", "APL-1.0", "Aladdin", - "ANTLR-PD", "Apache-1.0", "Apache-1.1", "Apache-2.0", "APSL-1.0", - "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0", "Artistic-1.0-cl8", - "Artistic-1.0-Perl", "Artistic-2.0", "AAL", "BitTorrent-1.0", - "BitTorrent-1.1", "BSL-1.0", "BSD-2-Clause", "BSD-2-Clause-FreeBSD", - "BSD-2-Clause-NetBSD", "BSD-3-Clause", "BSD-3-Clause-Clear", "BSD-4-Clause", - "BSD-4-Clause-UC", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", - "CECILL-C", "ClArtistic", "CNRI-Python", "CNRI-Python-GPL-Compatible", + "Glide", "Abstyles", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", + "AMPAS", "APL-1.0", "Adobe-Glyph", "APAFML", "Adobe-2006", "AGPL-1.0", + "Afmparse", "Aladdin", "ADSL", "AMDPLPA", "ANTLR-PD", "Apache-1.0", + "Apache-1.1", "Apache-2.0", "AML", "APSL-1.0", "APSL-1.1", "APSL-1.2", + "APSL-2.0", "Artistic-1.0", "Artistic-1.0-Perl", "Artistic-1.0-cl8", + "Artistic-2.0", "AAL", "Bahyph", "Barr", "Beerware", "BitTorrent-1.0", + "BitTorrent-1.1", "BSL-1.0", "Borceux", "BSD-2-Clause", + "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-3-Clause", + "BSD-3-Clause-Clear", "BSD-4-Clause", "BSD-Protection", + "BSD-3-Clause-Attribution", "BSD-4-Clause-UC", "bzip2-1.0.5", "bzip2-1.0.6", + "Caldera", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C", + "ClArtistic", "MIT-CMU", "CNRI-Python", "CNRI-Python-GPL-Compatible", "CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1", "Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0", - "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0", - "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", "CC-BY-NC-3.0", - "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", - "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", - "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0", "CC0-1.0", - "CUA-OPL-1.0", "D-FSL-1.0", "WTFPL", "EPL-1.0", "eCos-2.0", "ECL-1.0", - "ECL-2.0", "EFL-1.0", "EFL-2.0", "Entessa", "ErlPL-1.1", "EUDatagrid", - "EUPL-1.0", "EUPL-1.1", "Fair", "Frameworx-1.0", "FTL", "AGPL-1.0", - "AGPL-3.0", "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", - "GPL-2.0", "GPL-2.0+", "GPL-2.0-with-autoconf-exception", + "CC-BY-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0", + "CC-BY-ND-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", + "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", + "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0", + "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0", + "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0", + "CC-BY-SA-4.0", "CC0-1.0", "Crossword", "CUA-OPL-1.0", "Cube", "D-FSL-1.0", + "diffmark", "WTFPL", "DOC", "Dotseqn", "DSDP", "dvipdfm", "EPL-1.0", + "eCos-2.0", "ECL-1.0", "ECL-2.0", "eGenix", "EFL-1.0", "EFL-2.0", + "MIT-advertising", "MIT-enna", "Entessa", "ErlPL-1.1", "EUDatagrid", + "EUPL-1.0", "EUPL-1.1", "Eurosym", "Fair", "MIT-feh", "Frameworx-1.0", + "FTL", "FSFUL", "FSFULLR", "Giftware", "GL2PS", "Glulxe", "AGPL-3.0", + "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0", + "GPL-2.0+", "GPL-2.0-with-autoconf-exception", "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception", "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0", "GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception", "LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+", - "gSOAP-1.3b", "HPND", "IBM-pibs", "IPL-1.0", "Imlib2", "IJG", "Intel", - "IPA", "ISC", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", - "LPPL-1.3c", "Libpng", "LPL-1.02", "LPL-1.0", "MS-PL", "MS-RL", "MirOS", - "MIT", "Motosoto", "MPL-1.0", "MPL-1.1", "MPL-2.0", - "MPL-2.0-no-copyleft-exception", "Multics", "NASA-1.3", "Naumen", - "NBPL-1.0", "NGPL", "NOSL", "NPL-1.0", "NPL-1.1", "Nokia", "NPOSL-3.0", - "NTP", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2", + "gnuplot", "gSOAP-1.3b", "HaskellReport", "HPND", "IBM-pibs", "IPL-1.0", + "ImageMagick", "iMatix", "Imlib2", "IJG", "Intel-ACPI", "Intel", "IPA", + "ISC", "JasPer-2.0", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1", + "LPPL-1.2", "LPPL-1.3c", "Latex2e", "BSD-3-Clause-LBNL", "Leptonica", + "Libpng", "libtiff", "LPL-1.02", "LPL-1.0", "MakeIndex", "MTLL", "MS-PL", + "MS-RL", "MirOS", "MITNFA", "MIT", "Motosoto", "MPL-1.0", "MPL-1.1", + "MPL-2.0", "MPL-2.0-no-copyleft-exception", "mpich2", "Multics", "Mup", + "NASA-1.3", "Naumen", "NBPL-1.0", "NetCDF", "NGPL", "NOSL", "NPL-1.0", + "NPL-1.1", "Newsletr", "NLPL", "Nokia", "NPOSL-3.0", "Noweb", "NRL", "NTP", + "Nunit", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2", "OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0", "OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3", - "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OPL-1.0", "OSL-1.0", - "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8", "OpenSSL", "PHP-3.0", - "PHP-3.01", "PostgreSQL", "Python-2.0", "QPL-1.0", "RPSL-1.0", "RPL-1.1", - "RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "SGI-B-1.0", - "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1", "SimPL-2.0", "Sleepycat", - "SMLNJ", "SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", - "NCSA", "VSL-1.0", "W3C", "WXwindows", "Xnet", "X11", "XFree86-1.1", - "YPL-1.0", "YPL-1.1", "Zimbra-1.3", "Zlib", "ZPL-1.1", "ZPL-2.0", "ZPL-2.1", - "Unlicense" -] \ No newline at end of file + "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OML", "OPL-1.0", + "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8", + "OpenSSL", "PHP-3.0", "PHP-3.01", "Plexus", "PostgreSQL", "psfrag", + "psutils", "Python-2.0", "QPL-1.0", "Qhull", "Rdisc", "RPSL-1.0", "RPL-1.1", + "RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA", + "SWL", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1", + "SimPL-2.0", "Sleepycat", "SNIA", "SMLNJ", "StandardML-NJ", + "SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", "TCL", + "Unlicense", "TMate", "TORQUE-1.1", "TOSL", "Unicode-TOU", "NCSA", "Vim", + "VOSTROM", "VSL-1.0", "W3C", "Wsuipa", "WXwindows", "Xnet", "X11", "Xerox", + "XFree86-1.1", "xinetd", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed", + "Zend-2.0", "Zimbra-1.3", "Zlib", "zlib-acknowledgement", "ZPL-1.1", + "ZPL-2.0", "ZPL-2.1" +] From c571ccafd3daf39d980cc1159c035d6a6df8ddd0 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 18 Nov 2014 11:37:26 +0100 Subject: [PATCH 462/638] Restrict the JSONC fix to affected versions --- src/Composer/Json/JsonFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index e008ced9d..0611075e5 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -187,7 +187,7 @@ class JsonFile $json = json_encode($data, $options); // compact brackets to follow recent php versions - if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || defined('JSON_C_VERSION')) { + if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(php_version('json'), '1.3.6', '<'))) { $json = preg_replace('/\[\s+\]/', '[]', $json); $json = preg_replace('/\{\s+\}/', '{}', $json); } From b0f13e0ab603afe9d3c8baf5c958a41cd5187033 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 18 Nov 2014 11:39:47 +0100 Subject: [PATCH 463/638] Fix typo --- src/Composer/Json/JsonFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 0611075e5..ceaffaa25 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -187,7 +187,7 @@ class JsonFile $json = json_encode($data, $options); // compact brackets to follow recent php versions - if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(php_version('json'), '1.3.6', '<'))) { + if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(phpversion('json'), '1.3.6', '<'))) { $json = preg_replace('/\[\s+\]/', '[]', $json); $json = preg_replace('/\{\s+\}/', '{}', $json); } From ee56db87672453d9a8c050d1115f991e483f4806 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 14:33:46 +0100 Subject: [PATCH 464/638] Remove the handling of updates in the generator Updates are implicitly handled by whatProvides for install requests --- .../DependencyResolver/RuleSetGenerator.php | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index fa58687ea..ed0ab72a5 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -263,30 +263,6 @@ class RuleSetGenerator return $impossible; } - /** - * Adds all rules for all update packages of a given package - * - * @param PackageInterface $package Rules for this package's updates are to - * be added - */ - private function addRulesForUpdatePackages(PackageInterface $package, $ignorePlatformReqs) - { - $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package); - - foreach ($updates as $update) { - $this->addRulesForPackage($update, $ignorePlatformReqs); - } - } - - private function whitelistFromUpdatePackages(PackageInterface $package) - { - $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package, true); - - foreach ($updates as $update) { - $this->whitelistFromPackage($update); - } - } - protected function whitelistFromJobs() { foreach ($this->jobs as $job) { @@ -344,7 +320,6 @@ class RuleSetGenerator $this->whitelistedMap = array(); foreach ($this->installedMap as $package) { $this->whitelistFromPackage($package); - $this->whitelistFromUpdatePackages($package); } $this->whitelistFromJobs(); @@ -353,7 +328,6 @@ class RuleSetGenerator $this->addedMap = array(); foreach ($this->installedMap as $package) { $this->addRulesForPackage($package, $ignorePlatformReqs); - $this->addRulesForUpdatePackages($package, $ignorePlatformReqs); } $this->addRulesForJobs($ignorePlatformReqs); From 0daaa1a902fac09c1c5f3a33b8036ba3dc0ce125 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 14:42:56 +0100 Subject: [PATCH 465/638] Reduce whatProvides overhead --- src/Composer/DependencyResolver/Pool.php | 25 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 05645c87a..c385fda60 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -45,6 +45,7 @@ class Pool protected $providerRepos = array(); protected $packages = array(); protected $packageByName = array(); + protected $packageByExactName = array(); protected $acceptableStabilities; protected $stabilityFlags; protected $versionParser; @@ -122,6 +123,7 @@ class Pool $package['id'] = $this->id++; $package['stability'] = $stability; $this->packages[] = $package; + $this->packageByExactName[$package->getName()][$package['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$package['id']] = $this->packages[$this->id - 2]; @@ -144,6 +146,7 @@ class Pool $alias['id'] = $this->id++; $alias['root_alias'] = true; $this->packages[] = $alias; + $this->packageByExactName[$package->getName()][$alias['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; @@ -159,6 +162,7 @@ class Pool $alias['alias_of'] = $package['id']; $alias['id'] = $this->id++; $this->packages[] = $alias; + $this->packageByExactName[$package->getName()][$alias['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; @@ -173,6 +177,7 @@ class Pool if ($exempt || $this->isPackageAcceptable($names, $stability)) { $package->setId($this->id++); $this->packages[] = $package; + $this->packageByExactName[$package->getName()][$package->getId()] = $package; foreach ($names as $provided) { $this->packageByName[$provided][] = $package; @@ -191,6 +196,7 @@ class Pool $package->getRepository()->addPackage($aliasPackage); $this->packages[] = $aliasPackage; + $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->getId()] = $aliasPackage; foreach ($aliasPackage->getNames() as $name) { $this->packageByName[$name][] = $aliasPackage; @@ -261,8 +267,17 @@ class Pool } } - if (isset($this->packageByName[$name])) { - $candidates = array_merge($candidates, $this->packageByName[$name]); + if ($mustMatchName) { + $candidates = array_filter($candidates, function ($candidate) use ($name) { + return $candidate->getName() == $name; + }); + if (isset($this->packageByExactName[$name])) { + $candidates = array_merge($candidates, $this->packageByExactName[$name]); + } + } else { + if (isset($this->packageByName[$name])) { + $candidates = array_merge($candidates, $this->packageByName[$name]); + } } $matches = $provideMatches = array(); @@ -313,12 +328,6 @@ class Pool } } - if ($mustMatchName) { - return array_filter($matches, function ($match) use ($name) { - return $match->getName() == $name; - }); - } - // if a package with the required name exists, we ignore providers if ($nameMatch) { return $matches; From 83159dc153ac27287ee9902f2eadbb96a0ee8da4 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 14:59:41 +0100 Subject: [PATCH 466/638] Use elseif instead of else { if { --- src/Composer/DependencyResolver/Pool.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index c385fda60..13a233155 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -274,10 +274,8 @@ class Pool if (isset($this->packageByExactName[$name])) { $candidates = array_merge($candidates, $this->packageByExactName[$name]); } - } else { - if (isset($this->packageByName[$name])) { - $candidates = array_merge($candidates, $this->packageByName[$name]); - } + } elseif (isset($this->packageByName[$name])) { + $candidates = array_merge($candidates, $this->packageByName[$name]); } $matches = $provideMatches = array(); From 9751e1ab58a3bd40e986eee9f58ac57fa40b2e20 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Feb 2014 15:00:38 +0100 Subject: [PATCH 467/638] Remove unnecessary collection of names which isn't used --- .../Repository/ComposerRepository.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 134a47c2a..4bfe6db4e 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -381,25 +381,6 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } } } else { - if (isset($version['provide']) || isset($version['replace'])) { - // collect names - $names = array( - strtolower($version['name']) => true, - ); - if (isset($version['provide'])) { - foreach ($version['provide'] as $target => $constraint) { - $names[strtolower($target)] = true; - } - } - if (isset($version['replace'])) { - foreach ($version['replace'] as $target => $constraint) { - $names[strtolower($target)] = true; - } - } - $names = array_keys($names); - } else { - $names = array(strtolower($version['name'])); - } if (!$pool->isPackageAcceptable(strtolower($version['name']), VersionParser::parseStability($version['version']))) { continue; } From 670ca2f8892845eb94c75c20b46cd8d72ceaa085 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 20 Nov 2014 14:17:40 +0100 Subject: [PATCH 468/638] Fixed issue #3441 --- src/Composer/DependencyResolver/Pool.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 13a233155..8182819eb 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -123,7 +123,7 @@ class Pool $package['id'] = $this->id++; $package['stability'] = $stability; $this->packages[] = $package; - $this->packageByExactName[$package->getName()][$package['id']] = $this->packages[$this->id - 2]; + $this->packageByExactName[$name][$package['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$package['id']] = $this->packages[$this->id - 2]; @@ -146,7 +146,7 @@ class Pool $alias['id'] = $this->id++; $alias['root_alias'] = true; $this->packages[] = $alias; - $this->packageByExactName[$package->getName()][$alias['id']] = $this->packages[$this->id - 2]; + $this->packageByExactName[$name][$alias['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; @@ -162,7 +162,7 @@ class Pool $alias['alias_of'] = $package['id']; $alias['id'] = $this->id++; $this->packages[] = $alias; - $this->packageByExactName[$package->getName()][$alias['id']] = $this->packages[$this->id - 2]; + $this->packageByExactName[$name][$alias['id']] = $this->packages[$this->id - 2]; foreach ($names as $provided) { $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; From 89bd9be295376e450e388bee68a17ffbbeba0cad Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 20 Nov 2014 17:23:51 +0100 Subject: [PATCH 469/638] This entirely removes StreamableRepositories and minimal package arrays --- src/Composer/DependencyResolver/Pool.php | 104 +----------------- .../Repository/ComposerRepository.php | 70 +----------- .../StreamableRepositoryInterface.php | 61 ---------- 3 files changed, 5 insertions(+), 230 deletions(-) delete mode 100644 src/Composer/Repository/StreamableRepositoryInterface.php diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 8182819eb..56ee1f23c 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -22,7 +22,6 @@ use Composer\Repository\RepositoryInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\ComposerRepository; use Composer\Repository\InstalledRepositoryInterface; -use Composer\Repository\StreamableRepositoryInterface; use Composer\Repository\PlatformRepository; use Composer\Package\PackageInterface; @@ -97,79 +96,6 @@ class Pool $this->providerRepos[] = $repo; $repo->setRootAliases($rootAliases); $repo->resetPackageIds(); - } elseif ($repo instanceof StreamableRepositoryInterface) { - foreach ($repo->getMinimalPackages() as $package) { - $name = $package['name']; - $version = $package['version']; - $stability = VersionParser::parseStability($version); - - // collect names - $names = array( - $name => true, - ); - if (isset($package['provide'])) { - foreach ($package['provide'] as $target => $constraint) { - $names[$target] = true; - } - } - if (isset($package['replace'])) { - foreach ($package['replace'] as $target => $constraint) { - $names[$target] = true; - } - } - $names = array_keys($names); - - if ($exempt || $this->isPackageAcceptable($names, $stability)) { - $package['id'] = $this->id++; - $package['stability'] = $stability; - $this->packages[] = $package; - $this->packageByExactName[$name][$package['id']] = $this->packages[$this->id - 2]; - - foreach ($names as $provided) { - $this->packageByName[$provided][$package['id']] = $this->packages[$this->id - 2]; - } - - // handle root package aliases - unset($rootAliasData); - if (isset($rootAliases[$name][$version])) { - $rootAliasData = $rootAliases[$name][$version]; - } elseif (isset($package['alias_normalized']) && isset($rootAliases[$name][$package['alias_normalized']])) { - $rootAliasData = $rootAliases[$name][$package['alias_normalized']]; - } - - if (isset($rootAliasData)) { - $alias = $package; - unset($alias['raw']); - $alias['version'] = $rootAliasData['alias_normalized']; - $alias['alias'] = $rootAliasData['alias']; - $alias['alias_of'] = $package['id']; - $alias['id'] = $this->id++; - $alias['root_alias'] = true; - $this->packages[] = $alias; - $this->packageByExactName[$name][$alias['id']] = $this->packages[$this->id - 2]; - - foreach ($names as $provided) { - $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; - } - } - - // handle normal package aliases - if (isset($package['alias'])) { - $alias = $package; - unset($alias['raw']); - $alias['version'] = $package['alias_normalized']; - $alias['alias'] = $package['alias']; - $alias['alias_of'] = $package['id']; - $alias['id'] = $this->id++; - $this->packages[] = $alias; - $this->packageByExactName[$name][$alias['id']] = $this->packages[$this->id - 2]; - - foreach ($names as $provided) { - $this->packageByName[$provided][$alias['id']] = $this->packages[$this->id - 2]; - } - } - } - } } else { foreach ($repo->getPackages() as $package) { $names = $package->getNames(); @@ -227,7 +153,7 @@ class Pool */ public function packageById($id) { - return $this->ensurePackageIsLoaded($this->packages[$id - 1]); + return $this->packages[$id - 1]; } /** @@ -307,15 +233,15 @@ class Pool case self::MATCH: $nameMatch = true; - $matches[] = $this->ensurePackageIsLoaded($candidate); + $matches[] = $candidate; break; case self::MATCH_PROVIDE: - $provideMatches[] = $this->ensurePackageIsLoaded($candidate); + $provideMatches[] = $candidate; break; case self::MATCH_REPLACE: - $matches[] = $this->ensurePackageIsLoaded($candidate); + $matches[] = $candidate; break; case self::MATCH_FILTERED: @@ -376,28 +302,6 @@ class Pool return false; } - private function ensurePackageIsLoaded($data) - { - if (is_array($data)) { - if (isset($data['alias_of'])) { - $aliasOf = $this->packageById($data['alias_of']); - $package = $this->packages[$data['id'] - 1] = $data['repo']->loadAliasPackage($data, $aliasOf); - $package->setRootPackageAlias(!empty($data['root_alias'])); - } else { - $package = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data); - } - - foreach ($package->getNames() as $name) { - $this->packageByName[$name][$data['id']] = $package; - } - $package->setId($data['id']); - - return $package; - } - - return $data; - } - /** * Checks if the package matches the given constraint directly or through * provided or replaced packages diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 4bfe6db4e..459fa55a7 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -30,7 +30,7 @@ use Composer\EventDispatcher\EventDispatcher; /** * @author Jordi Boggiano */ -class ComposerRepository extends ArrayRepository implements StreamableRepositoryInterface +class ComposerRepository extends ArrayRepository { protected $config; protected $options; @@ -165,49 +165,6 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository return parent::getPackages(); } - /** - * {@inheritDoc} - */ - public function getMinimalPackages() - { - if (isset($this->minimalPackages)) { - return $this->minimalPackages; - } - - if (null === $this->rawData) { - $this->rawData = $this->loadDataFromServer(); - } - - $this->minimalPackages = array(); - $versionParser = new VersionParser; - - foreach ($this->rawData as $package) { - $version = !empty($package['version_normalized']) ? $package['version_normalized'] : $versionParser->normalize($package['version']); - $data = array( - 'name' => strtolower($package['name']), - 'repo' => $this, - 'version' => $version, - 'raw' => $package, - ); - if (!empty($package['replace'])) { - $data['replace'] = $package['replace']; - } - if (!empty($package['provide'])) { - $data['provide'] = $package['provide']; - } - - // add branch aliases - if ($aliasNormalized = $this->loader->getBranchAlias($package)) { - $data['alias'] = preg_replace('{(\.9{7})+}', '.x', $aliasNormalized); - $data['alias_normalized'] = $aliasNormalized; - } - - $this->minimalPackages[] = $data; - } - - return $this->minimalPackages; - } - /** * {@inheritDoc} */ @@ -262,20 +219,6 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository return $providers; } - /** - * {@inheritDoc} - */ - public function loadPackage(array $data) - { - $package = $this->createPackage($data['raw'], 'Composer\Package\Package'); - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } - $package->setRepository($this); - - return $package; - } - protected function configurePackageTransportOptions(PackageInterface $package) { foreach ($package->getDistUrls() as $url) { @@ -287,17 +230,6 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository } } - /** - * {@inheritDoc} - */ - public function loadAliasPackage(array $data, PackageInterface $aliasOf) - { - $aliasPackage = $this->createAliasPackage($aliasOf, $data['version'], $data['alias']); - $aliasPackage->setRepository($this); - - return $aliasPackage; - } - public function hasProviders() { $this->loadRootServerFile(); diff --git a/src/Composer/Repository/StreamableRepositoryInterface.php b/src/Composer/Repository/StreamableRepositoryInterface.php deleted file mode 100644 index f5c694642..000000000 --- a/src/Composer/Repository/StreamableRepositoryInterface.php +++ /dev/null @@ -1,61 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository; - -use Composer\Package\AliasPackage; -use Composer\Package\PackageInterface; - -/** - * @author Jordi Boggiano - */ -interface StreamableRepositoryInterface extends RepositoryInterface -{ - /** - * Return partial package data without loading them all to save on memory - * - * The function must return an array of package arrays. - * - * The package array must contain the following fields: - * - name: package name (normalized/lowercased) - * - repo: reference to the repository instance - * - version: normalized version - * - replace: array of package name => version constraint, optional - * - provide: array of package name => version constraint, optional - * - alias: pretty alias that this package should be aliased to, optional - * - alias_normalized: normalized alias that this package should be aliased to, optional - * - * Any additional information can be returned and will be sent back - * into loadPackage/loadAliasPackage for completing the package loading - * when it's needed. - * - * @return array - */ - public function getMinimalPackages(); - - /** - * Loads a package from minimal info of the package - * - * @param array $data the minimal info as was returned by getMinimalPackage - * @return PackageInterface - */ - public function loadPackage(array $data); - - /** - * Loads an alias package from minimal info of the package - * - * @param array $data the minimal info as was returned by getMinimalPackage - * @param PackageInterface $aliasOf the package which this alias is an alias of - * @return AliasPackage - */ - public function loadAliasPackage(array $data, PackageInterface $aliasOf); -} From dccb728e1050a5a4530f28f342d266924a7271bb Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 20 Nov 2014 17:42:05 +0100 Subject: [PATCH 470/638] Remove further unnecessary checks for packages being arrays --- src/Composer/DependencyResolver/Pool.php | 35 ++++++------------------ 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 56ee1f23c..98553abc4 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -217,9 +217,8 @@ class Pool } if ($this->whitelist !== null && ( - (is_array($candidate) && isset($candidate['id']) && !isset($this->whitelist[$candidate['id']])) || - (is_object($candidate) && !($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->getId()])) || - (is_object($candidate) && $candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->getId()])) + (!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->getId()])) || + ($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->getId()])) )) { continue; } @@ -313,19 +312,10 @@ class Pool */ private function match($candidate, $name, LinkConstraintInterface $constraint = null) { - // handle array packages - if (is_array($candidate)) { - $candidateName = $candidate['name']; - $candidateVersion = $candidate['version']; - $isDev = $candidate['stability'] === 'dev'; - $isAlias = isset($candidate['alias_of']); - } else { - // handle object packages - $candidateName = $candidate->getName(); - $candidateVersion = $candidate->getVersion(); - $isDev = $candidate->getStability() === 'dev'; - $isAlias = $candidate instanceof AliasPackage; - } + $candidateName = $candidate->getName(); + $candidateVersion = $candidate->getVersion(); + $isDev = $candidate->getStability() === 'dev'; + $isAlias = $candidate instanceof AliasPackage; if (!$isDev && !$isAlias && isset($this->filterRequires[$name])) { $requireFilter = $this->filterRequires[$name]; @@ -343,17 +333,8 @@ class Pool return self::MATCH_NAME; } - if (is_array($candidate)) { - $provides = isset($candidate['provide']) - ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'provides', $candidate['provide']) - : array(); - $replaces = isset($candidate['replace']) - ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'replaces', $candidate['replace']) - : array(); - } else { - $provides = $candidate->getProvides(); - $replaces = $candidate->getReplaces(); - } + $provides = $candidate->getProvides(); + $replaces = $candidate->getReplaces(); // aliases create multiple replaces/provides for one target so they can not use the shortcut below if (isset($replaces[0]) || isset($provides[0])) { From 2e40f5f5863c462148fa656f5b53ff6092797da0 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 20 Nov 2014 15:41:29 +0000 Subject: [PATCH 471/638] Add failing test case --- .../install-branch-alias-composer-repo.test | 19 +++++++++++++++ .../packages.json | 23 +++++++++++++++++++ tests/Composer/Test/InstallerTest.php | 15 ++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo.test create mode 100644 tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo/packages.json diff --git a/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo.test b/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo.test new file mode 100644 index 000000000..bad64fc7d --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo.test @@ -0,0 +1,19 @@ +--TEST-- +Installing branch aliased package from a Composer repository. +--COMPOSER-- +{ + "repositories": [ + { + "type": "composer", + "url": "file://install-branch-alias-composer-repo" + } + ], + "require": { + "a/a": "3.2.*@dev" + } +} +--RUN-- +install +--EXPECT-- +Installing a/a (dev-foobar abcdef0) +Marking a/a (3.2.x-dev abcdef0) as installed, alias of a/a (dev-foobar abcdef0) diff --git a/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo/packages.json b/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo/packages.json new file mode 100644 index 000000000..d51c0be9f --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-branch-alias-composer-repo/packages.json @@ -0,0 +1,23 @@ +{ + "packages": { + "a/a": { + "dev-foobar": { + "name": "a/a", + "version": "dev-foobar", + "version_normalized": "dev-foobar", + "source": { + "type": "git", + "url": "git@example.com:repo.git", + "reference": "abcdef0000000000000000000000000000000000" + }, + "time": "2014-11-13 11:52:12", + "type": "library", + "extra": { + "branch-alias": { + "dev-foobar": "3.2.x-dev" + } + } + } + } + } +} diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 40dd03313..ee306a818 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -283,6 +283,21 @@ class InstallerTest extends TestCase $message = $match['test']; $condition = !empty($match['condition']) ? $match['condition'] : null; $composer = JsonFile::parseJson($match['composer']); + + if (isset($composer['repositories'])) { + foreach ($composer['repositories'] as &$repo) { + if ($repo['type'] !== 'composer') { + continue; + } + + if (preg_match('{^file://[^/]}', $repo['url'])) { + $repo['url'] = "file://${fixturesDir}/" . substr($repo['url'], 7); + } + + unset($repo); + } + } + if (!empty($match['lock'])) { $lock = JsonFile::parseJson($match['lock']); if (!isset($lock['hash'])) { From 0f9128a7d26b035473ed40a6bc0ce80209a3efa8 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 20 Nov 2014 16:13:55 +0000 Subject: [PATCH 472/638] Add comment --- tests/Composer/Test/InstallerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index ee306a818..9983b8c47 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -290,6 +290,7 @@ class InstallerTest extends TestCase continue; } + // Change paths like file://foobar to file:///path/to/fixtures if (preg_match('{^file://[^/]}', $repo['url'])) { $repo['url'] = "file://${fixturesDir}/" . substr($repo['url'], 7); } From b84c3f0268d0aad8ba6752a536f3f6532dc62983 Mon Sep 17 00:00:00 2001 From: Craig Duncan Date: Mon, 8 Sep 2014 11:44:36 +0100 Subject: [PATCH 473/638] Don't display the dev warning time when running self-update Use the Application::find() method to resolve the command name, as Application::getCommandName() only returns whatever was entered as the first argument. If the user only entered enough of the command to be unambiguous (eg self-up) then the warning would still show By calling Application::find() we ensure that if we are going to run the self-update command then we will not show the warning, no matter what the user entered --- src/Composer/Console/Application.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 4ecb8b1a3..e0037f655 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -94,9 +94,17 @@ class Application extends BaseApplication $output->writeln('Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.'); } - if (defined('COMPOSER_DEV_WARNING_TIME') && $this->getCommandName($input) !== 'self-update' && $this->getCommandName($input) !== 'selfupdate') { - if (time() > COMPOSER_DEV_WARNING_TIME) { - $output->writeln(sprintf('Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); + if (defined('COMPOSER_DEV_WARNING_TIME')) { + $commandName = ''; + if ($name = $this->getCommandName($input)) { + try { + $commandName = $this->find($name)->getName(); + } catch (\InvalidArgumentException $e) {} + } + if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { + if (time() > COMPOSER_DEV_WARNING_TIME) { + $output->writeln(sprintf('Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); + } } } From 647625ddc5b8dbec551cf47756acb919b6f34735 Mon Sep 17 00:00:00 2001 From: Craig Duncan Date: Mon, 8 Sep 2014 19:24:04 +0100 Subject: [PATCH 474/638] Added some tests for the dev version warning --- tests/Composer/Test/ApplicationTest.php | 76 +++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tests/Composer/Test/ApplicationTest.php diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php new file mode 100644 index 000000000..c99022671 --- /dev/null +++ b/tests/Composer/Test/ApplicationTest.php @@ -0,0 +1,76 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test; + +use Composer\Console\Application; +use Composer\TestCase; + +class ApplicationTest extends TestCase +{ + public function testDevWarning() + { + $application = new Application; + + $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $inputMock->expects($this->once()) + ->method('getFirstArgument') + ->will($this->returnValue('list')); + + $outputMock->expects($this->once()) + ->method("writeln") + ->with($this->equalTo(sprintf('Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF']))); + + if (!defined('COMPOSER_DEV_WARNING_TIME')) { + define('COMPOSER_DEV_WARNING_TIME', time() - 1); + } + + $this->setExpectedException('RuntimeException'); + $application->doRun($inputMock, $outputMock); + } + + public function ensureNoDevWarning($command) + { + $application = new Application; + + $application->add(new \Composer\Command\SelfUpdateCommand); + + $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $inputMock->expects($this->once()) + ->method('getFirstArgument') + ->will($this->returnValue($command)); + + $outputMock->expects($this->never()) + ->method("writeln"); + + if (!defined('COMPOSER_DEV_WARNING_TIME')) { + define('COMPOSER_DEV_WARNING_TIME', time() - 1); + } + + $this->setExpectedException('RuntimeException'); + $application->doRun($inputMock, $outputMock); + } + + public function testDevWarningPrevented() + { + $this->ensureNoDevWarning('self-update'); + } + + public function testDevWarningPreventedAlias() + { + $this->ensureNoDevWarning('self-up'); + } +} From 725a4fd638c7f339cdf5f1daaff0899bdbca24d3 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 21 Nov 2014 10:07:56 +0000 Subject: [PATCH 475/638] Backup/restore Composer environment variables The tests could fail if an environment variable replaces an expected value. --- tests/Composer/Test/ConfigTest.php | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 705fe78e0..47ee08718 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -16,6 +16,39 @@ use Composer\Config; class ConfigTest extends \PHPUnit_Framework_TestCase { + private static $envVars = array( + 'VENDOR_DIR', + 'BIN_DIR', + 'PROCESS_TIMEOUT', + 'CACHE_DIR', + 'CACHE_FILES_DIR', + 'CACHE_REPO_DIR', + 'CACHE_VCS_DIR', + 'DISCARD_CHANGES', + ); + + private $envVarValues = array(); + + public function setUp() + { + foreach (self::$envVars as $var) { + $var = 'COMPOSER_' . $var; + + if ($value = getenv($var)) { + $this->envVarValues[$var] = $value; + + putenv($var . '='); + } + } + } + + public function tearDown() + { + foreach ($this->envVarValues as $var => $value) { + putenv(sprintf('%s=%s', $var, $value)); + } + } + /** * @dataProvider dataAddPackagistRepository */ From 5a56ebd54511b6acfaadd0c7008bfc3155c2c18d Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 21 Nov 2014 10:09:21 +0000 Subject: [PATCH 476/638] Revert "Backup/restore Composer environment variables" This reverts commit 725a4fd638c7f339cdf5f1daaff0899bdbca24d3. --- tests/Composer/Test/ConfigTest.php | 33 ------------------------------ 1 file changed, 33 deletions(-) diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 47ee08718..705fe78e0 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -16,39 +16,6 @@ use Composer\Config; class ConfigTest extends \PHPUnit_Framework_TestCase { - private static $envVars = array( - 'VENDOR_DIR', - 'BIN_DIR', - 'PROCESS_TIMEOUT', - 'CACHE_DIR', - 'CACHE_FILES_DIR', - 'CACHE_REPO_DIR', - 'CACHE_VCS_DIR', - 'DISCARD_CHANGES', - ); - - private $envVarValues = array(); - - public function setUp() - { - foreach (self::$envVars as $var) { - $var = 'COMPOSER_' . $var; - - if ($value = getenv($var)) { - $this->envVarValues[$var] = $value; - - putenv($var . '='); - } - } - } - - public function tearDown() - { - foreach ($this->envVarValues as $var => $value) { - putenv(sprintf('%s=%s', $var, $value)); - } - } - /** * @dataProvider dataAddPackagistRepository */ From 86b5938cdb2b0bd43161a3f88f72108e053d572c Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 21 Nov 2014 10:14:40 +0000 Subject: [PATCH 477/638] Allow reading of COMPOSER_ environment variables to be disabled --- src/Composer/Config.php | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index e740bb8c7..bd5d86895 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -58,12 +58,17 @@ class Config private $repositories; private $configSource; private $authConfigSource; + private $useEnvironment; - public function __construct() + /** + * @param boolean $useEnvironment Use COMPOSER_ environment variables to replace config settings + */ + public function __construct($useEnvironment = true) { // load defaults $this->config = static::$defaultConfig; $this->repositories = static::$defaultRepositories; + $this->useEnvironment = (bool) $useEnvironment; } public function setConfigSource(ConfigSourceInterface $source) @@ -159,7 +164,7 @@ class Config // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_')); - $val = rtrim($this->process(getenv($env) ?: $this->config[$key]), '/\\'); + $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key]), '/\\'); $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val); return $val; @@ -201,7 +206,7 @@ class Config return rtrim($this->process($this->config[$key]), '/\\'); case 'discard-changes': - if ($env = getenv('COMPOSER_DISCARD_CHANGES')) { + if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) { if (!in_array($env, array('stash', 'true', 'false', '1', '0'), true)) { throw new \RuntimeException( "Invalid value for COMPOSER_DISCARD_CHANGES: {$env}. Expected 1, 0, true, false or stash" @@ -288,4 +293,22 @@ class Config return $config->get($match[1]); }, $value); } + + /** + * Reads the value of a Composer environment variable + * + * This should be used to read COMPOSER_ environment variables + * that overload config values. + * + * @param string $var + * @return string|boolean + */ + private function getComposerEnv($var) + { + if ($this->useEnvironment) { + return getenv($var); + } + + return false; + } } From c819bd7e705f7937552e2fee374fdf254b992919 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 21 Nov 2014 10:15:17 +0000 Subject: [PATCH 478/638] Update config tests to not use environment variables --- tests/Composer/Test/ConfigTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 705fe78e0..521fb634b 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -21,7 +21,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase */ public function testAddPackagistRepository($expected, $localConfig, $systemConfig = null) { - $config = new Config(); + $config = new Config(false); if ($systemConfig) { $config->merge(array('repositories' => $systemConfig)); } @@ -102,7 +102,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testMergeGithubOauth() { - $config = new Config(); + $config = new Config(false); $config->merge(array('config' => array('github-oauth' => array('foo' => 'bar')))); $config->merge(array('config' => array('github-oauth' => array('bar' => 'baz')))); @@ -111,7 +111,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testVarReplacement() { - $config = new Config(); + $config = new Config(false); $config->merge(array('config' => array('a' => 'b', 'c' => '{$a}'))); $config->merge(array('config' => array('bin-dir' => '$HOME', 'cache-dir' => '~/foo/'))); @@ -123,7 +123,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testOverrideGithubProtocols() { - $config = new Config(); + $config = new Config(false); $config->merge(array('config' => array('github-protocols' => array('https', 'git')))); $config->merge(array('config' => array('github-protocols' => array('https')))); From 5333017aeaee2d344d9ae557e3b5d9bdc2d7575e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 21 Nov 2014 15:26:09 +0100 Subject: [PATCH 479/638] Remove unused selectNewestPackages method in DefaultPolicy Unused as of 1b6f57e6515f6a81edd11572686eb389183f2636 --- .../DependencyResolver/DefaultPolicy.php | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index a58cf6184..0bbd6b9b4 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -215,26 +215,6 @@ class DefaultPolicy implements PolicyInterface return $bestLiterals; } - protected function selectNewestPackages(array $installedMap, array $literals) - { - $maxLiterals = array($literals[0]); - $maxPackage = $literals[0]->getPackage(); - foreach ($literals as $i => $literal) { - if (0 === $i) { - continue; - } - - if ($this->versionCompare($literal->getPackage(), $maxPackage, '>')) { - $maxPackage = $literal->getPackage(); - $maxLiterals = array($literal); - } elseif ($this->versionCompare($literal->getPackage(), $maxPackage, '==')) { - $maxLiterals[] = $literal; - } - } - - return $maxLiterals; - } - /** * Assumes that installed packages come first and then all highest priority packages */ From 5896f283be3fdf588ee94f90d0953c5c728679a1 Mon Sep 17 00:00:00 2001 From: "Matthew J. Sahagian" Date: Fri, 21 Nov 2014 13:16:19 -0800 Subject: [PATCH 480/638] Allow for disabling autoloader on Installer --- src/Composer/Installer.php | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b76155a5a..3654cddd3 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -105,6 +105,7 @@ class Installer protected $dryRun = false; protected $verbose = false; protected $update = false; + protected $runAutoloader = true; protected $runScripts = true; protected $ignorePlatformReqs = false; /** @@ -311,16 +312,18 @@ class Installer } } - // write autoloader - if ($this->optimizeAutoloader) { - $this->io->write('Generating optimized autoload files'); - } else { - $this->io->write('Generating autoload files'); + if ($this->runAutoloader) { + // write autoloader + if ($this->optimizeAutoloader) { + $this->io->write('Generating optimized autoload files'); + } else { + $this->io->write('Generating autoload files'); + } + + $this->autoloadGenerator->setDevMode($this->devMode); + $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } - $this->autoloadGenerator->setDevMode($this->devMode); - $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); - if ($this->runScripts) { // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; @@ -1152,6 +1155,21 @@ class Installer return $this; } + + /** + * set whether to run autoloader or not + * + * @param boolean $runAutoloader + * @return Installer + */ + public function setRunAutoloader($runAutoloader = true) + { + $this->runAutoloader = (boolean) $runAutoloader; + + return $this; + } + + /** * set whether to run scripts or not * From 198ac7bc5b52460c8bab140226a6273327565d3b Mon Sep 17 00:00:00 2001 From: "Matthew J. Sahagian" Date: Fri, 21 Nov 2014 13:20:48 -0800 Subject: [PATCH 481/638] Expose no autoloader option to install command --- src/Composer/Command/InstallCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 669c03582..c1de25ce7 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -41,6 +41,7 @@ class InstallCommand extends Command new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), + new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), @@ -113,6 +114,7 @@ EOT ->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) + ->setRunAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) From 55b2afe9ad5da07277caa1061f06e47f70d24328 Mon Sep 17 00:00:00 2001 From: "Matthew J. Sahagian" Date: Fri, 21 Nov 2014 13:22:17 -0800 Subject: [PATCH 482/638] Expose no autoloader option to update command --- src/Composer/Command/UpdateCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index ebfd4cb2a..bebde6ed1 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -41,6 +41,7 @@ class UpdateCommand extends Command new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'), new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), + new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'), @@ -115,6 +116,7 @@ EOT ->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) + ->setRunAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) ->setUpdate(true) From 14fe882b4459252ffa09d3944575a21b659c4e42 Mon Sep 17 00:00:00 2001 From: "Matthew J. Sahagian" Date: Fri, 21 Nov 2014 13:28:44 -0800 Subject: [PATCH 483/638] Added no-autoloder option to install and update --- doc/03-cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7fcafc2d6..0607a45b3 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -88,6 +88,7 @@ resolution. installation and show you what would happen. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. +* **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-plugins:** Disables plugins. * **--no-progress:** Removes the progress display that can mess with some @@ -130,6 +131,7 @@ php composer.phar update vendor/* * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. +* **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-plugins:** Disables plugins. * **--no-progress:** Removes the progress display that can mess with some From 62bcc3996b7301c4ef2ed44d50457e939ed8bf92 Mon Sep 17 00:00:00 2001 From: Paul Schell Date: Tue, 25 Nov 2014 17:01:07 +0100 Subject: [PATCH 484/638] Update 00-intro.md --- doc/00-intro.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/00-intro.md b/doc/00-intro.md index 81b8c9295..4e744c1d1 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -127,6 +127,9 @@ Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe it will install the latest Composer version and set up your PATH so that you can just call `composer` from any directory in your command line. +> **Note:** Close your current terminal. Test usage with a new terminal: +> Thats important because the PATH gets only loaded when the terminal gets started. + ### Manual Installation Change to a directory on your `PATH` and run the install snippet to download From d884b26aa357dc955c6eefb395b769bc1ceca48c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 26 Nov 2014 19:51:13 +0100 Subject: [PATCH 485/638] Add test case for removing unused dependencies on whitelisted update --- .../update-whitelist-removes-unused.test | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test new file mode 100644 index 000000000..e658e8c06 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test @@ -0,0 +1,32 @@ +--TEST-- +Update with a package whitelist removes unused packages +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "whitelisted", "version": "1.1.0" }, + { "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } }, + { "name": "fixed-dependency", "version": "1.1.0" }, + { "name": "fixed-dependency", "version": "1.0.0" }, + { "name": "old-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "whitelisted": "1.*", + "fixed-dependency": "1.*" + } +} +--INSTALLED-- +[ + { "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } }, + { "name": "fixed-dependency", "version": "1.0.0" }, + { "name": "old-dependency", "version": "1.0.0" } +] +--RUN-- +update --with-dependencies whitelisted +--EXPECT-- +Uninstalling old-dependency (1.0.0) +Updating whitelisted (1.0.0) to whitelisted (1.1.0) From 540dfacfa073bebab5d4a094fa080d9250deb407 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 26 Nov 2014 20:24:30 +0100 Subject: [PATCH 486/638] Add test to verify that depency packages get deleted when no longer in lock file --- .../install-from-lock-removes-package.test | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test diff --git a/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test b/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test new file mode 100644 index 000000000..6063abfee --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test @@ -0,0 +1,44 @@ +--TEST-- +Install from a lock file that deleted a package +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "whitelisted", "version": "1.1.0" }, + { "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } }, + { "name": "fixed-dependency", "version": "1.1.0" }, + { "name": "fixed-dependency", "version": "1.0.0" }, + { "name": "old-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "whitelisted": "1.*", + "fixed-dependency": "1.*" + } +} +--LOCK-- +{ + "packages": [ + { "name": "whitelisted", "version": "1.1.0" }, + { "name": "fixed-dependency", "version": "1.0.0" } + ], + "packages-dev": null, + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false +} +--INSTALLED-- +[ + { "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } }, + { "name": "fixed-dependency", "version": "1.0.0" }, + { "name": "old-dependency", "version": "1.0.0" } +] +--RUN-- +install +--EXPECT-- +Uninstalling old-dependency (1.0.0) +Updating whitelisted (1.0.0) to whitelisted (1.1.0) From acf363876263e0eca620a77d2cfa5342f44e6217 Mon Sep 17 00:00:00 2001 From: Mads Kristensen Date: Fri, 28 Nov 2014 14:02:05 -0800 Subject: [PATCH 487/638] Converted to draft v4 schema Editors such as Visual Studio leverages JSON Schema draft v4, but this schema was authored in the old draft v3. With just a few minor changes, the schema is now draft v4. * Added `$schema` property to state it is using schema draft v4 * Changed `required` properties to arrays instead of strings. This is the only real difference from v3 to v4 in this schema --- res/composer-schema.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index 95cd199e3..49ec0fa7a 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -1,12 +1,13 @@ { + "$schema": "http://json-schema.org/draft-04/schema#", "name": "Package", "type": "object", "additionalProperties": false, + "required": [ "name", "description" ], "properties": { "name": { "type": "string", - "description": "Package name, including 'vendor-name/' prefix.", - "required": true + "description": "Package name, including 'vendor-name/' prefix." }, "type": { "description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.", @@ -18,8 +19,7 @@ }, "description": { "type": "string", - "description": "Short package description.", - "required": true + "description": "Short package description." }, "keywords": { "type": "array", @@ -51,11 +51,11 @@ "items": { "type": "object", "additionalProperties": false, + "required": [ "name"], "properties": { "name": { "type": "string", - "description": "Full name of the author.", - "required": true + "description": "Full name of the author." }, "email": { "type": "string", From ef41f136f842b6b47f3c9b83418f436dc36b0af0 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 18:05:10 +0100 Subject: [PATCH 488/638] Literals on rule are now public This causes a reduction of ~500k function calls for packagist composer update (~14 million total). --- src/Composer/DependencyResolver/Rule.php | 8 ++------ src/Composer/DependencyResolver/RuleWatchGraph.php | 2 +- src/Composer/DependencyResolver/RuleWatchNode.php | 4 ++-- src/Composer/DependencyResolver/Solver.php | 14 +++++++------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index a47038431..e592e3c16 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -32,7 +32,6 @@ class Rule protected $pool; protected $disabled; - protected $literals; protected $type; protected $id; protected $reason; @@ -42,6 +41,8 @@ class Rule protected $ruleHash; + public $literals; + public function __construct(Pool $pool, array $literals, $reason, $reasonData, $job = null) { $this->pool = $pool; @@ -160,11 +161,6 @@ class Rule return !$this->disabled; } - public function getLiterals() - { - return $this->literals; - } - public function isAssertion() { return 1 === count($this->literals); diff --git a/src/Composer/DependencyResolver/RuleWatchGraph.php b/src/Composer/DependencyResolver/RuleWatchGraph.php index a9f7414b2..2360c5219 100644 --- a/src/Composer/DependencyResolver/RuleWatchGraph.php +++ b/src/Composer/DependencyResolver/RuleWatchGraph.php @@ -95,7 +95,7 @@ class RuleWatchGraph $otherWatch = $node->getOtherWatch($literal); if (!$node->getRule()->isDisabled() && !$decisions->satisfy($otherWatch)) { - $ruleLiterals = $node->getRule()->getLiterals(); + $ruleLiterals = $node->getRule()->literals; $alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $decisions) { return $literal !== $ruleLiteral && diff --git a/src/Composer/DependencyResolver/RuleWatchNode.php b/src/Composer/DependencyResolver/RuleWatchNode.php index 59482ffdb..cdbf6a00b 100644 --- a/src/Composer/DependencyResolver/RuleWatchNode.php +++ b/src/Composer/DependencyResolver/RuleWatchNode.php @@ -35,7 +35,7 @@ class RuleWatchNode { $this->rule = $rule; - $literals = $rule->getLiterals(); + $literals = $rule->literals; $this->watch1 = count($literals) > 0 ? $literals[0] : 0; $this->watch2 = count($literals) > 1 ? $literals[1] : 0; @@ -51,7 +51,7 @@ class RuleWatchNode */ public function watch2OnHighest(Decisions $decisions) { - $literals = $this->rule->getLiterals(); + $literals = $this->rule->literals; // if there are only 2 elements, both are being watched anyway if (count($literals) < 3) { diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 36b1d9862..3d9249b05 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -62,7 +62,7 @@ class Solver continue; } - $literals = $rule->getLiterals(); + $literals = $rule->literals; $literal = $literals[0]; if (!$this->decisions->decided(abs($literal))) { @@ -104,7 +104,7 @@ class Solver continue; } - $assertRuleLiterals = $assertRule->getLiterals(); + $assertRuleLiterals = $assertRule->literals; $assertRuleLiteral = $assertRuleLiterals[0]; if (abs($literal) !== abs($assertRuleLiteral)) { @@ -356,7 +356,7 @@ class Solver while (true) { $this->learnedPool[count($this->learnedPool) - 1][] = $rule; - foreach ($rule->getLiterals() as $literal) { + foreach ($rule->literals as $literal) { // skip the one true literal if ($this->decisions->satisfy($literal)) { continue; @@ -480,7 +480,7 @@ class Solver $this->problems[] = $problem; $seen = array(); - $literals = $conflictRule->getLiterals(); + $literals = $conflictRule->literals; foreach ($literals as $literal) { // skip the one true literal @@ -503,7 +503,7 @@ class Solver $problem->addRule($why); $this->analyzeUnsolvableRule($problem, $why); - $literals = $why->getLiterals(); + $literals = $why->literals; foreach ($literals as $literal) { // skip the one true literal @@ -627,7 +627,7 @@ class Solver $decisionQueue = array(); $noneSatisfied = true; - foreach ($rule->getLiterals() as $literal) { + foreach ($rule->literals as $literal) { if ($this->decisions->satisfy($literal)) { $noneSatisfied = false; break; @@ -688,7 +688,7 @@ class Solver } $rule = $this->rules->ruleById($i); - $literals = $rule->getLiterals(); + $literals = $rule->literals; if ($rule->isDisabled()) { continue; From c6af09b1da95ec9c64f4e7fecd7a7a41a3ad3aaf Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 18:18:02 +0100 Subject: [PATCH 489/638] Make project id public Reduction of rougly 1.3 million function calls on packagist update --- .../DependencyResolver/DefaultPolicy.php | 10 +++---- src/Composer/DependencyResolver/Pool.php | 12 ++++----- .../DependencyResolver/RuleSetGenerator.php | 22 ++++++++-------- src/Composer/DependencyResolver/Solver.php | 8 +++--- .../DependencyResolver/Transaction.php | 26 +++++++++---------- src/Composer/Package/BasePackage.php | 3 ++- 6 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 0bbd6b9b4..10cbe101e 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -151,18 +151,18 @@ class DefaultPolicy implements PolicyInterface } // priority equal, sort by package id to make reproducible - if ($a->getId() === $b->getId()) { + if ($a->id === $b->id) { return 0; } - return ($a->getId() < $b->getId()) ? -1 : 1; + return ($a->id < $b->id) ? -1 : 1; } - if (isset($installedMap[$a->getId()])) { + if (isset($installedMap[$a->id])) { return -1; } - if (isset($installedMap[$b->getId()])) { + if (isset($installedMap[$b->id])) { return 1; } @@ -227,7 +227,7 @@ class DefaultPolicy implements PolicyInterface foreach ($literals as $literal) { $package = $pool->literalToPackage($literal); - if (isset($installedMap[$package->getId()])) { + if (isset($installedMap[$package->id])) { $selected[] = $literal; continue; } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 98553abc4..e0d2d8a13 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -103,7 +103,7 @@ class Pool if ($exempt || $this->isPackageAcceptable($names, $stability)) { $package->setId($this->id++); $this->packages[] = $package; - $this->packageByExactName[$package->getName()][$package->getId()] = $package; + $this->packageByExactName[$package->getName()][$package->id] = $package; foreach ($names as $provided) { $this->packageByName[$provided][] = $package; @@ -122,7 +122,7 @@ class Pool $package->getRepository()->addPackage($aliasPackage); $this->packages[] = $aliasPackage; - $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->getId()] = $aliasPackage; + $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage; foreach ($aliasPackage->getNames() as $name) { $this->packageByName[$name][] = $aliasPackage; @@ -186,7 +186,7 @@ class Pool foreach ($this->providerRepos as $repo) { foreach ($repo->whatProvides($this, $name) as $candidate) { $candidates[] = $candidate; - if ($candidate->getId() < 1) { + if ($candidate->id < 1) { $candidate->setId($this->id++); $this->packages[$this->id - 2] = $candidate; } @@ -217,8 +217,8 @@ class Pool } if ($this->whitelist !== null && ( - (!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->getId()])) || - ($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->getId()])) + (!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->id])) || + ($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->id])) )) { continue; } @@ -275,7 +275,7 @@ class Pool { $package = $this->literalToPackage($literal); - if (isset($installedMap[$package->getId()])) { + if (isset($installedMap[$package->id])) { $prefix = ($literal > 0 ? 'keep' : 'remove'); } else { $prefix = ($literal > 0 ? 'install' : 'don\'t install'); diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index ed0ab72a5..1f47ed20d 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -51,14 +51,14 @@ class RuleSetGenerator */ protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null) { - $literals = array(-$package->getId()); + $literals = array(-$package->id); foreach ($providers as $provider) { // self fulfilling rule? if ($provider === $package) { return null; } - $literals[] = $provider->getId(); + $literals[] = $provider->id; } return new Rule($this->pool, $literals, $reason, $reasonData); @@ -80,7 +80,7 @@ class RuleSetGenerator { $literals = array(); foreach ($packages as $package) { - $literals[] = $package->getId(); + $literals[] = $package->id; } return new Rule($this->pool, $literals, $reason, $job['packageName'], $job); @@ -99,7 +99,7 @@ class RuleSetGenerator */ protected function createRemoveRule(PackageInterface $package, $reason, $job) { - return new Rule($this->pool, array(-$package->getId()), $reason, $job['packageName'], $job); + return new Rule($this->pool, array(-$package->id), $reason, $job['packageName'], $job); } /** @@ -123,7 +123,7 @@ class RuleSetGenerator return null; } - return new Rule($this->pool, array(-$issuer->getId(), -$provider->getId()), $reason, $reasonData); + return new Rule($this->pool, array(-$issuer->id, -$provider->id), $reason, $reasonData); } /** @@ -151,11 +151,11 @@ class RuleSetGenerator while (!$workQueue->isEmpty()) { $package = $workQueue->dequeue(); - if (isset($this->whitelistedMap[$package->getId()])) { + if (isset($this->whitelistedMap[$package->id])) { continue; } - $this->whitelistedMap[$package->getId()] = true; + $this->whitelistedMap[$package->id] = true; foreach ($package->getRequires() as $link) { $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint(), true); @@ -186,11 +186,11 @@ class RuleSetGenerator while (!$workQueue->isEmpty()) { $package = $workQueue->dequeue(); - if (isset($this->addedMap[$package->getId()])) { + if (isset($this->addedMap[$package->id])) { continue; } - $this->addedMap[$package->getId()] = true; + $this->addedMap[$package->id] = true; foreach ($package->getRequires() as $link) { if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { @@ -215,7 +215,7 @@ class RuleSetGenerator } // check obsoletes and implicit obsoletes of a package - $isInstalled = (isset($this->installedMap[$package->getId()])); + $isInstalled = (isset($this->installedMap[$package->id])); foreach ($package->getReplaces() as $link) { $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); @@ -289,7 +289,7 @@ class RuleSetGenerator $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); if ($packages) { foreach ($packages as $package) { - if (!isset($this->installedMap[$package->getId()])) { + if (!isset($this->installedMap[$package->id])) { $this->addRulesForPackage($package, $ignorePlatformReqs); } } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 3d9249b05..9dc375787 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -125,7 +125,7 @@ class Solver { $this->installedMap = array(); foreach ($this->installed->getPackages() as $package) { - $this->installedMap[$package->getId()] = $package; + $this->installedMap[$package->id] = $package; } } @@ -136,15 +136,15 @@ class Solver case 'update': $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); foreach ($packages as $package) { - if (isset($this->installedMap[$package->getId()])) { - $this->updateMap[$package->getId()] = true; + if (isset($this->installedMap[$package->id])) { + $this->updateMap[$package->id] = true; } } break; case 'update-all': foreach ($this->installedMap as $package) { - $this->updateMap[$package->getId()] = true; + $this->updateMap[$package->id] = true; } break; diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index b9569b3fa..b847164ff 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -49,7 +49,7 @@ class Transaction $package = $this->pool->literalToPackage($literal); // wanted & installed || !wanted & !installed - if (($literal > 0) == (isset($this->installedMap[$package->getId()]))) { + if (($literal > 0) == (isset($this->installedMap[$package->id]))) { continue; } @@ -57,7 +57,7 @@ class Transaction if (isset($installMeansUpdateMap[abs($literal)]) && !$package instanceof AliasPackage) { $source = $installMeansUpdateMap[abs($literal)]; - $updateMap[$package->getId()] = array( + $updateMap[$package->id] = array( 'package' => $package, 'source' => $source, 'reason' => $reason, @@ -65,9 +65,9 @@ class Transaction // avoid updates to one package from multiple origins unset($installMeansUpdateMap[abs($literal)]); - $ignoreRemove[$source->getId()] = true; + $ignoreRemove[$source->id] = true; } else { - $installMap[$package->getId()] = array( + $installMap[$package->id] = array( 'package' => $package, 'reason' => $reason, ); @@ -81,9 +81,9 @@ class Transaction $package = $this->pool->literalToPackage($literal); if ($literal <= 0 && - isset($this->installedMap[$package->getId()]) && - !isset($ignoreRemove[$package->getId()])) { - $uninstallMap[$package->getId()] = array( + isset($this->installedMap[$package->id]) && + !isset($ignoreRemove[$package->id])) { + $uninstallMap[$package->id] = array( 'package' => $package, 'reason' => $reason, ); @@ -107,7 +107,7 @@ class Transaction while (!empty($queue)) { $package = array_pop($queue); - $packageId = $package->getId(); + $packageId = $package->id; if (!isset($visited[$packageId])) { array_push($queue, $package); @@ -124,7 +124,7 @@ class Transaction } } - $visited[$package->getId()] = true; + $visited[$package->id] = true; } else { if (isset($installMap[$packageId])) { $this->install( @@ -165,7 +165,7 @@ class Transaction $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); foreach ($possibleRequires as $require) { - unset($roots[$require->getId()]); + unset($roots[$require->id]); } } } @@ -186,13 +186,13 @@ class Transaction } // !wanted & installed - if ($literal <= 0 && isset($this->installedMap[$package->getId()])) { + if ($literal <= 0 && isset($this->installedMap[$package->id])) { $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package); - $literals = array($package->getId()); + $literals = array($package->id); foreach ($updates as $update) { - $literals[] = $update->getId(); + $literals[] = $update->id; } foreach ($literals as $updateLiteral) { diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index d756196ef..0590a6931 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -48,9 +48,10 @@ abstract class BasePackage implements PackageInterface protected $prettyName; protected $repository; - protected $id; protected $transportOptions; + public $id; + /** * All descendants' constructors should call this parent constructor * From 5c0efdcaa2f5b77673bd3d13c0172e8f05a836f1 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 18:25:22 +0100 Subject: [PATCH 490/638] Move public properties to top and document them --- src/Composer/DependencyResolver/Rule.php | 12 ++++++++++-- src/Composer/Package/BasePackage.php | 8 +++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index e592e3c16..922aa5735 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -29,6 +29,16 @@ class Rule const RULE_LEARNED = 12; const RULE_PACKAGE_ALIAS = 13; + /** + * The literals this rule consists of. + * + * Each element is a package id either positive for installation or + * negative meaning removal. + * + * @var array + */ + public $literals; + protected $pool; protected $disabled; @@ -41,8 +51,6 @@ class Rule protected $ruleHash; - public $literals; - public function __construct(Pool $pool, array $literals, $reason, $reasonData, $job = null) { $this->pool = $pool; diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 0590a6931..e0f0eb22c 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -44,13 +44,19 @@ abstract class BasePackage implements PackageInterface 'dev' => self::STABILITY_DEV, ); + /** + * The package id, public for fast access in dependency solver + * Use getId() unless called extremely frequently. + * @var int + */ + public $id; + protected $name; protected $prettyName; protected $repository; protected $transportOptions; - public $id; /** * All descendants' constructors should call this parent constructor From d77400ade29b087e4d0a726859d7760fd3c971c8 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 18:28:45 +0100 Subject: [PATCH 491/638] Make ruleById lookup table in rule set public Saves about 500k function calls on a packagist update --- src/Composer/DependencyResolver/RuleSet.php | 13 +++++++------ src/Composer/DependencyResolver/Solver.php | 4 ++-- .../Test/DependencyResolver/RuleSetTest.php | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 05ab780ac..1b72204a0 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -22,6 +22,13 @@ class RuleSet implements \IteratorAggregate, \Countable const TYPE_JOB = 1; const TYPE_LEARNED = 4; + /** + * Lookup table for rule id to rule object + * + * @var array + */ + public $ruleById; + protected static $types = array( -1 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', @@ -30,7 +37,6 @@ class RuleSet implements \IteratorAggregate, \Countable ); protected $rules; - protected $ruleById; protected $nextRuleId; protected $rulesByHash; @@ -76,11 +82,6 @@ class RuleSet implements \IteratorAggregate, \Countable return $this->nextRuleId; } - public function ruleById($id) - { - return $this->ruleById[$id]; - } - public function getRules() { return $this->rules; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 9dc375787..c9a4d5882 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -56,7 +56,7 @@ class Solver $rulesCount = count($this->rules); for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) { - $rule = $this->rules->ruleById($ruleIndex); + $rule = $this->rules->ruleById[$ruleIndex]; if (!$rule->isAssertion() || $rule->isDisabled()) { continue; @@ -687,7 +687,7 @@ class Solver $i = 0; } - $rule = $this->rules->ruleById($i); + $rule = $this->rules->ruleById[$i]; $literals = $rule->literals; if ($rule->isDisabled()) { diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 35e3f17d6..a124aef60 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -76,7 +76,7 @@ class RuleSetTest extends TestCase $rule = new Rule($this->pool, array(), 'job1', null); $ruleSet->add($rule, RuleSet::TYPE_JOB); - $this->assertSame($rule, $ruleSet->ruleById(0)); + $this->assertSame($rule, $ruleSet->ruleById[0]); } public function testGetIterator() From 26598c4a9ad1944314f8750a7caccacc6c4451d7 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 19:02:50 +0100 Subject: [PATCH 492/638] Remove unnecessary pool reference from rules --- src/Composer/DependencyResolver/Problem.php | 2 +- src/Composer/DependencyResolver/Rule.php | 36 ++++++++----------- src/Composer/DependencyResolver/RuleSet.php | 9 +++-- .../DependencyResolver/RuleSetGenerator.php | 8 ++--- src/Composer/DependencyResolver/Solver.php | 4 +-- .../RuleSetIteratorTest.php | 6 ++-- .../Test/DependencyResolver/RuleSetTest.php | 32 ++++++++--------- .../Test/DependencyResolver/RuleTest.php | 32 ++++++++--------- 8 files changed, 64 insertions(+), 65 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 765b74a19..9373a7b7b 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -130,7 +130,7 @@ class Problem $messages[] = $this->jobToText($job); } elseif ($rule) { if ($rule instanceof Rule) { - $messages[] = $rule->getPrettyString($installedMap); + $messages[] = $rule->getPrettyString($this->pool, $installedMap); } } } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 922aa5735..58136f447 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -31,16 +31,10 @@ class Rule /** * The literals this rule consists of. - * - * Each element is a package id either positive for installation or - * negative meaning removal. - * * @var array */ public $literals; - protected $pool; - protected $disabled; protected $type; protected $id; @@ -51,10 +45,8 @@ class Rule protected $ruleHash; - public function __construct(Pool $pool, array $literals, $reason, $reasonData, $job = null) + public function __construct(array $literals, $reason, $reasonData, $job = null) { - $this->pool = $pool; - // sort all packages ascending by id sort($literals); @@ -174,14 +166,14 @@ class Rule return 1 === count($this->literals); } - public function getPrettyString(array $installedMap = array()) + public function getPrettyString(Pool $pool, array $installedMap = array()) { $ruleText = ''; foreach ($this->literals as $i => $literal) { if ($i != 0) { $ruleText .= '|'; } - $ruleText .= $this->pool->literalToPrettyString($literal, $installedMap); + $ruleText .= $pool->literalToPrettyString($literal, $installedMap); } switch ($this->reason) { @@ -195,24 +187,24 @@ class Rule return "Remove command rule ($ruleText)"; case self::RULE_PACKAGE_CONFLICT: - $package1 = $this->pool->literalToPackage($this->literals[0]); - $package2 = $this->pool->literalToPackage($this->literals[1]); + $package1 = $pool->literalToPackage($this->literals[0]); + $package2 = $pool->literalToPackage($this->literals[1]); - return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique(array($package2)).'.'; + return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique($pool, array($package2)).'.'; case self::RULE_PACKAGE_REQUIRES: $literals = $this->literals; $sourceLiteral = array_shift($literals); - $sourcePackage = $this->pool->literalToPackage($sourceLiteral); + $sourcePackage = $pool->literalToPackage($sourceLiteral); $requires = array(); foreach ($literals as $literal) { - $requires[] = $this->pool->literalToPackage($literal); + $requires[] = $pool->literalToPackage($literal); } $text = $this->reasonData->getPrettyString($sourcePackage); if ($requires) { - $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($requires) . '.'; + $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires) . '.'; } else { $targetName = $this->reasonData->getTarget(); @@ -239,22 +231,24 @@ class Rule case self::RULE_INSTALLED_PACKAGE_OBSOLETES: return $ruleText; case self::RULE_PACKAGE_SAME_NAME: - return 'Can only install one of: ' . $this->formatPackagesUnique($this->literals) . '.'; + return 'Can only install one of: ' . $this->formatPackagesUnique($pool, $this->literals) . '.'; case self::RULE_PACKAGE_IMPLICIT_OBSOLETES: return $ruleText; case self::RULE_LEARNED: return 'Conclusion: '.$ruleText; case self::RULE_PACKAGE_ALIAS: return $ruleText; + default: + return '('.$ruleText.')'; } } - protected function formatPackagesUnique(array $packages) + protected function formatPackagesUnique($pool, array $packages) { $prepared = array(); foreach ($packages as $package) { if (!is_object($package)) { - $package = $this->pool->literalToPackage($package); + $package = $pool->literalToPackage($package); } $prepared[$package->getName()]['name'] = $package->getPrettyName(); $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion(); @@ -279,7 +273,7 @@ class Rule if ($i != 0) { $result .= '|'; } - $result .= $this->pool->literalToString($literal); + $result .= $literal; } $result .= ')'; diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 1b72204a0..0b237736f 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -145,17 +145,22 @@ class RuleSet implements \IteratorAggregate, \Countable return false; } - public function __toString() + public function getPrettyString(Pool $pool = null) { $string = "\n"; foreach ($this->rules as $type => $rules) { $string .= str_pad(self::$types[$type], 8, ' ') . ": "; foreach ($rules as $rule) { - $string .= $rule."\n"; + $string .= ($pool ? $rule->getPrettyString($pool) : $rule)."\n"; } $string .= "\n\n"; } return $string; } + + public function __toString() + { + return $this->getPrettyString(null); + } } diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 1f47ed20d..8ea24e742 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -61,7 +61,7 @@ class RuleSetGenerator $literals[] = $provider->id; } - return new Rule($this->pool, $literals, $reason, $reasonData); + return new Rule($literals, $reason, $reasonData); } /** @@ -83,7 +83,7 @@ class RuleSetGenerator $literals[] = $package->id; } - return new Rule($this->pool, $literals, $reason, $job['packageName'], $job); + return new Rule($literals, $reason, $job['packageName'], $job); } /** @@ -99,7 +99,7 @@ class RuleSetGenerator */ protected function createRemoveRule(PackageInterface $package, $reason, $job) { - return new Rule($this->pool, array(-$package->id), $reason, $job['packageName'], $job); + return new Rule(array(-$package->id), $reason, $job['packageName'], $job); } /** @@ -123,7 +123,7 @@ class RuleSetGenerator return null; } - return new Rule($this->pool, array(-$issuer->id, -$provider->id), $reason, $reasonData); + return new Rule(array(-$issuer->id, -$provider->id), $reason, $reasonData); } /** diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index c9a4d5882..19e69913b 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -155,7 +155,7 @@ class Solver if (!$this->pool->whatProvides($job['packageName'], $job['constraint'])) { $problem = new Problem($this->pool); - $problem->addRule(new Rule($this->pool, array(), null, null, $job)); + $problem->addRule(new Rule(array(), null, null, $job)); $this->problems[] = $problem; } break; @@ -441,7 +441,7 @@ class Solver ); } - $newRule = new Rule($this->pool, $learnedLiterals, Rule::RULE_LEARNED, $why); + $newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why); return array($learnedLiterals[0], $ruleLevel, $newRule, $why); } diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 1598e7717..e83b79c2f 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -27,11 +27,11 @@ class RuleSetIteratorTest extends \PHPUnit_Framework_TestCase $this->rules = array( RuleSet::TYPE_JOB => array( - new Rule($this->pool, array(), 'job1', null), - new Rule($this->pool, array(), 'job2', null), + new Rule(array(), 'job1', null), + new Rule(array(), 'job2', null), ), RuleSet::TYPE_LEARNED => array( - new Rule($this->pool, array(), 'update1', null), + new Rule(array(), 'update1', null), ), RuleSet::TYPE_PACKAGE => array(), ); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index a124aef60..c1eb83e5e 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -32,11 +32,11 @@ class RuleSetTest extends TestCase $rules = array( RuleSet::TYPE_PACKAGE => array(), RuleSet::TYPE_JOB => array( - new Rule($this->pool, array(), 'job1', null), - new Rule($this->pool, array(), 'job2', null), + new Rule(array(), 'job1', null), + new Rule(array(), 'job2', null), ), RuleSet::TYPE_LEARNED => array( - new Rule($this->pool, array(), 'update1', null), + new Rule(array(), 'update1', null), ), ); @@ -56,15 +56,15 @@ class RuleSetTest extends TestCase { $ruleSet = new RuleSet; - $ruleSet->add(new Rule($this->pool, array(), 'job1', null), 7); + $ruleSet->add(new Rule(array(), 'job1', null), 7); } public function testCount() { $ruleSet = new RuleSet; - $ruleSet->add(new Rule($this->pool, array(), 'job1', null), RuleSet::TYPE_JOB); - $ruleSet->add(new Rule($this->pool, array(), 'job2', null), RuleSet::TYPE_JOB); + $ruleSet->add(new Rule(array(), 'job1', null), RuleSet::TYPE_JOB); + $ruleSet->add(new Rule(array(), 'job2', null), RuleSet::TYPE_JOB); $this->assertEquals(2, $ruleSet->count()); } @@ -73,7 +73,7 @@ class RuleSetTest extends TestCase { $ruleSet = new RuleSet; - $rule = new Rule($this->pool, array(), 'job1', null); + $rule = new Rule(array(), 'job1', null); $ruleSet->add($rule, RuleSet::TYPE_JOB); $this->assertSame($rule, $ruleSet->ruleById[0]); @@ -83,8 +83,8 @@ class RuleSetTest extends TestCase { $ruleSet = new RuleSet; - $rule1 = new Rule($this->pool, array(), 'job1', null); - $rule2 = new Rule($this->pool, array(), 'job1', null); + $rule1 = new Rule(array(), 'job1', null); + $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); @@ -98,8 +98,8 @@ class RuleSetTest extends TestCase public function testGetIteratorFor() { $ruleSet = new RuleSet; - $rule1 = new Rule($this->pool, array(), 'job1', null); - $rule2 = new Rule($this->pool, array(), 'job1', null); + $rule1 = new Rule(array(), 'job1', null); + $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); @@ -112,8 +112,8 @@ class RuleSetTest extends TestCase public function testGetIteratorWithout() { $ruleSet = new RuleSet; - $rule1 = new Rule($this->pool, array(), 'job1', null); - $rule2 = new Rule($this->pool, array(), 'job1', null); + $rule1 = new Rule(array(), 'job1', null); + $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); @@ -155,7 +155,7 @@ class RuleSetTest extends TestCase $this->assertFalse($ruleSet->containsEqual($rule3)); } - public function testToString() + public function testPrettyString() { $repo = new ArrayRepository; $repo->addPackage($p = $this->getPackage('foo', '2.1')); @@ -163,11 +163,11 @@ class RuleSetTest extends TestCase $ruleSet = new RuleSet; $literal = $p->getId(); - $rule = new Rule($this->pool, array($literal), 'job1', null); + $rule = new Rule(array($literal), 'job1', null); $ruleSet->add($rule, RuleSet::TYPE_JOB); - $this->assertContains('JOB : (+foo-2.1.0.0)', $ruleSet->__toString()); + $this->assertContains('JOB : (install foo 2.1)', $ruleSet->getPrettyString($this->pool)); } private function getRuleMock() diff --git a/tests/Composer/Test/DependencyResolver/RuleTest.php b/tests/Composer/Test/DependencyResolver/RuleTest.php index 10667632d..6688b24aa 100644 --- a/tests/Composer/Test/DependencyResolver/RuleTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleTest.php @@ -28,14 +28,14 @@ class RuleTest extends TestCase public function testGetHash() { - $rule = new Rule($this->pool, array(123), 'job1', null); + $rule = new Rule(array(123), 'job1', null); $this->assertEquals(substr(md5('123'), 0, 5), $rule->getHash()); } public function testSetAndGetId() { - $rule = new Rule($this->pool, array(), 'job1', null); + $rule = new Rule(array(), 'job1', null); $rule->setId(666); $this->assertEquals(666, $rule->getId()); @@ -43,31 +43,31 @@ class RuleTest extends TestCase public function testEqualsForRulesWithDifferentHashes() { - $rule = new Rule($this->pool, array(1, 2), 'job1', null); - $rule2 = new Rule($this->pool, array(1, 3), 'job1', null); + $rule = new Rule(array(1, 2), 'job1', null); + $rule2 = new Rule(array(1, 3), 'job1', null); $this->assertFalse($rule->equals($rule2)); } public function testEqualsForRulesWithDifferLiteralsQuantity() { - $rule = new Rule($this->pool, array(1, 12), 'job1', null); - $rule2 = new Rule($this->pool, array(1), 'job1', null); + $rule = new Rule(array(1, 12), 'job1', null); + $rule2 = new Rule(array(1), 'job1', null); $this->assertFalse($rule->equals($rule2)); } public function testEqualsForRulesWithSameLiterals() { - $rule = new Rule($this->pool, array(1, 12), 'job1', null); - $rule2 = new Rule($this->pool, array(1, 12), 'job1', null); + $rule = new Rule(array(1, 12), 'job1', null); + $rule2 = new Rule(array(1, 12), 'job1', null); $this->assertTrue($rule->equals($rule2)); } public function testSetAndGetType() { - $rule = new Rule($this->pool, array(), 'job1', null); + $rule = new Rule(array(), 'job1', null); $rule->setType('someType'); $this->assertEquals('someType', $rule->getType()); @@ -75,7 +75,7 @@ class RuleTest extends TestCase public function testEnable() { - $rule = new Rule($this->pool, array(), 'job1', null); + $rule = new Rule(array(), 'job1', null); $rule->disable(); $rule->enable(); @@ -85,7 +85,7 @@ class RuleTest extends TestCase public function testDisable() { - $rule = new Rule($this->pool, array(), 'job1', null); + $rule = new Rule(array(), 'job1', null); $rule->enable(); $rule->disable(); @@ -95,22 +95,22 @@ class RuleTest extends TestCase public function testIsAssertions() { - $rule = new Rule($this->pool, array(1, 12), 'job1', null); - $rule2 = new Rule($this->pool, array(1), 'job1', null); + $rule = new Rule(array(1, 12), 'job1', null); + $rule2 = new Rule(array(1), 'job1', null); $this->assertFalse($rule->isAssertion()); $this->assertTrue($rule2->isAssertion()); } - public function testToString() + public function testPrettyString() { $repo = new ArrayRepository; $repo->addPackage($p1 = $this->getPackage('foo', '2.1')); $repo->addPackage($p2 = $this->getPackage('baz', '1.1')); $this->pool->addRepository($repo); - $rule = new Rule($this->pool, array($p1->getId(), -$p2->getId()), 'job1', null); + $rule = new Rule(array($p1->getId(), -$p2->getId()), 'job1', null); - $this->assertEquals('(-baz-1.1.0.0|+foo-2.1.0.0)', $rule->__toString()); + $this->assertEquals('(don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($this->pool)); } } From 107a736440768849ad3518a572bd24dc44c7e631 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 19:04:35 +0100 Subject: [PATCH 493/638] Recreate getLiterals method for BC --- src/Composer/DependencyResolver/Rule.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 58136f447..9c1fc0b48 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -161,6 +161,14 @@ class Rule return !$this->disabled; } + /** + * @deprecated Use public literals member + */ + public function getLiterals() + { + return $this->literals; + } + public function isAssertion() { return 1 === count($this->literals); From 2c43a3bcd09b72630d2ce8981bf41afd6499735e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 19:07:26 +0100 Subject: [PATCH 494/638] Improve docblocks of public properties --- src/Composer/DependencyResolver/Rule.php | 2 +- src/Composer/DependencyResolver/RuleSet.php | 4 ++-- src/Composer/Package/BasePackage.php | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 9c1fc0b48..cba9400ef 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -30,7 +30,7 @@ class Rule const RULE_PACKAGE_ALIAS = 13; /** - * The literals this rule consists of. + * READ-ONLY: The literals this rule consists of. * @var array */ public $literals; diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 0b237736f..439a438b5 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -23,9 +23,9 @@ class RuleSet implements \IteratorAggregate, \Countable const TYPE_LEARNED = 4; /** - * Lookup table for rule id to rule object + * READ-ONLY: Lookup table for rule id to rule object * - * @var array + * @var Rule[] */ public $ruleById; diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index e0f0eb22c..677d5074d 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -45,8 +45,7 @@ abstract class BasePackage implements PackageInterface ); /** - * The package id, public for fast access in dependency solver - * Use getId() unless called extremely frequently. + * READ-ONLY: The package id, public for fast access in dependency solver * @var int */ public $id; From 6f41e99fe29bdf8e0470b5c92d1cb5eafc119eb4 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 19:11:02 +0100 Subject: [PATCH 495/638] Add back ruleById function for BC --- src/Composer/DependencyResolver/RuleSet.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 439a438b5..b9545123f 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -82,6 +82,11 @@ class RuleSet implements \IteratorAggregate, \Countable return $this->nextRuleId; } + public function ruleById($id) + { + return $this->ruleById[$id]; + } + public function getRules() { return $this->rules; From 4a945da55b406c88c33770cde316675efc3c2862 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 1 Dec 2014 19:21:33 +0100 Subject: [PATCH 496/638] Avoid generating duplicate conflict rules For each version of each package we create a conflict rule with each other version. These are then added to the rule set and skipped if duplicate so instead we can just generate them only once to begin with and avoid unnecessary memory allocation and duplication lookups. --- src/Composer/DependencyResolver/RuleSetGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 8ea24e742..30e3a0964 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -241,7 +241,7 @@ class RuleSetGenerator if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package)); - } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) { + } elseif (!$this->obsoleteImpossibleForAlias($package, $provider) && $package->id <= $provider->id) { $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, $package)); } From 7fd93414649a2e08b25d3215040b654e34bfd542 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 1 Dec 2014 22:56:59 +0100 Subject: [PATCH 497/638] Fix the display of rules Close #3483 --- src/Composer/Installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b76155a5a..06f593620 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -574,11 +574,11 @@ class Installer if ($reason instanceof Rule) { switch ($reason->getReason()) { case Rule::RULE_JOB_INSTALL: - $this->io->write(' REASON: Required by root: '.$reason->getPrettyString()); + $this->io->write(' REASON: Required by root: '.$reason->getPrettyString($pool)); $this->io->write(''); break; case Rule::RULE_PACKAGE_REQUIRES: - $this->io->write(' REASON: '.$reason->getPrettyString()); + $this->io->write(' REASON: '.$reason->getPrettyString($pool)); $this->io->write(''); break; } From ac676f47f7bbc619678a29deae097b6b0710b799 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Dec 2014 10:23:21 +0000 Subject: [PATCH 498/638] Disable GC when computing deps, refs #3482 --- src/Composer/Installer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b76155a5a..1b2a6772e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -160,6 +160,8 @@ class Installer */ public function run() { + gc_disable(); + if ($this->dryRun) { $this->verbose = true; $this->runScripts = false; From 557381f49ff1b875810446757a8dc79a6b4a69fd Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 2 Dec 2014 11:39:01 +0100 Subject: [PATCH 499/638] Fix the Travis configuration exit should not be used in script commands, because it exits the whole build script. Thus, turning a failure exit code into another failure exit code is not needed. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b019c1926..d11676714 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_script: - git config --global user.email travis@example.com script: - - ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml {};' || exit 1 + - ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml {};' git: - depth: 5 \ No newline at end of file + depth: 5 From ea4441247634dc01874bcae89f9e4fcb132917c4 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 2 Dec 2014 14:31:37 +0100 Subject: [PATCH 500/638] Skip the phar building on HHVM Building a phar does not work on HHVM. It looks like it tries to read the phar file which does not exist yet. --- tests/Composer/Test/AllFunctionalTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index f27d58117..39f0c23d6 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -60,6 +60,10 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase public function testBuildPhar() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('Building the phar does not work on HHVM.'); + } + $fs = new Filesystem; $fs->removeDirectory(dirname(self::$pharPath)); $fs->ensureDirectoryExists(dirname(self::$pharPath)); From 68bcba3a036c0418fc3639a38496b910dfacbfeb Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 2 Dec 2014 14:32:37 +0100 Subject: [PATCH 501/638] Disallow failures on HHVM on Travis --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d11676714..4aed11625 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,6 @@ php: - 5.6 - hhvm -matrix: - allow_failures: - - php: hhvm - before_script: - sudo apt-get install parallel - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini From bf210a340bd7403d40e29d2199140362e5ab9a6b Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Tue, 2 Dec 2014 16:02:20 +0200 Subject: [PATCH 502/638] Free up memory before disabling gc. --- src/Composer/Installer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 5d292e771..9e2c07021 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -160,6 +160,7 @@ class Installer */ public function run() { + gc_collect_cycles(); gc_disable(); if ($this->dryRun) { From a63495415a2bf897ddfa6ac9c40d65cd375f15a0 Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Tue, 2 Dec 2014 15:41:25 +0100 Subject: [PATCH 503/638] Readme: Use an SVG badge. They look better on retina screens and when zoomed in. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf07c54b5..a86bc83f6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Composer helps you declare, manage and install dependencies of PHP projects, ens See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation. -[![Build Status](https://secure.travis-ci.org/composer/composer.png?branch=master)](http://travis-ci.org/composer/composer) +[![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer) Installation / Usage -------------------- From dfe18d2e7f31dd71e0489f4a218ea638c06fcde5 Mon Sep 17 00:00:00 2001 From: Steve Preston Date: Tue, 2 Dec 2014 11:46:56 -0500 Subject: [PATCH 504/638] Fixed dependency problem caused by pull request #3482 --- src/Composer/DependencyResolver/RuleSetGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 30e3a0964..8ea24e742 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -241,7 +241,7 @@ class RuleSetGenerator if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package)); - } elseif (!$this->obsoleteImpossibleForAlias($package, $provider) && $package->id <= $provider->id) { + } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) { $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, $package)); } From c57f761675c8147f1c4b214290a518acf8c63a48 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 2 Dec 2014 19:18:44 +0000 Subject: [PATCH 505/638] Upgraded phpunit --- composer.json | 4 +- composer.lock | 502 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 419 insertions(+), 87 deletions(-) diff --git a/composer.json b/composer.json index 718b06780..6d239970d 100644 --- a/composer.json +++ b/composer.json @@ -24,13 +24,13 @@ "require": { "php": ">=5.3.2", "justinrainbow/json-schema": "~1.1", - "seld/jsonlint": "1.*", + "seld/jsonlint": "~1.0", "symfony/console": "~2.3", "symfony/finder": "~2.2", "symfony/process": "~2.1" }, "require-dev": { - "phpunit/phpunit": "~3.7" + "phpunit/phpunit": "~4.0" }, "suggest": { "ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic", diff --git a/composer.lock b/composer.lock index cfeea277e..b13b38f37 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "eeb4afc3be46412ec15842ce4af01f0b", + "hash": "0430383b5ba00e406ce4a44253f49fe7", "packages": [ { "name": "justinrainbow/json-schema", @@ -120,17 +120,17 @@ }, { "name": "symfony/console", - "version": "v2.5.4", + "version": "v2.6.0", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1" + "reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/748beed2a1e73179c3f5154d33fe6ae100c1aeb1", - "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1", + "url": "https://api.github.com/repos/symfony/Console/zipball/d3bac228fd7a2aac9193e241b239880b3ba39a10", + "reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10", "shasum": "" }, "require": { @@ -138,16 +138,18 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1" + "symfony/event-dispatcher": "~2.1", + "symfony/process": "~2.1" }, "suggest": { "psr/log": "For using the console logger", - "symfony/event-dispatcher": "" + "symfony/event-dispatcher": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } }, "autoload": { @@ -171,21 +173,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-08-14 16:10:54" + "time": "2014-11-20 13:24:23" }, { "name": "symfony/finder", - "version": "v2.5.4", + "version": "v2.6.0", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", "url": "https://github.com/symfony/Finder.git", - "reference": "f40854d1a19c339c7f969f8f6d6d6e9153311c4c" + "reference": "d574347c652a14cfee0349f744c7880e1d9029fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/f40854d1a19c339c7f969f8f6d6d6e9153311c4c", - "reference": "f40854d1a19c339c7f969f8f6d6d6e9153311c4c", + "url": "https://api.github.com/repos/symfony/Finder/zipball/d574347c652a14cfee0349f744c7880e1d9029fd", + "reference": "d574347c652a14cfee0349f744c7880e1d9029fd", "shasum": "" }, "require": { @@ -194,7 +196,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } }, "autoload": { @@ -218,21 +220,21 @@ ], "description": "Symfony Finder Component", "homepage": "http://symfony.com", - "time": "2014-09-03 09:00:14" + "time": "2014-11-28 10:00:40" }, { "name": "symfony/process", - "version": "v2.5.5", + "version": "v2.6.0", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b" + "reference": "dc88f75d1c07791e5733f90be747961dce26cf05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/8a1ec96c4e519cee0fb971ea48a1eb7369dda54b", - "reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b", + "url": "https://api.github.com/repos/symfony/Process/zipball/dc88f75d1c07791e5733f90be747961dce26cf05", + "reference": "dc88f75d1c07791e5733f90be747961dce26cf05", "shasum": "" }, "require": { @@ -241,7 +243,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } }, "autoload": { @@ -265,46 +267,104 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-09-23 05:25:11" + "time": "2014-11-04 14:29:39" } ], "packages-dev": [ { - "name": "phpunit/php-code-coverage", - "version": "1.2.18", + "name": "doctrine/instantiator", + "version": "1.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", - "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.2.0@stable", - "phpunit/php-token-stream": ">=1.1.3,<1.3.0" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { - "phpunit/phpunit": "3.7.*@dev" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.0.5" + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "2.0.*@ALPHA" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Instantiator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2014-10-13 12:58:55" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.0.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7ce9da20f96964bb7a4033f53834df13328dbeab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ce9da20f96964bb7a4033f53834df13328dbeab", + "reference": "7ce9da20f96964bb7a4033f53834df13328dbeab", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "~1.0", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4.1" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -328,7 +388,7 @@ "testing", "xunit" ], - "time": "2014-09-02 10:13:14" + "time": "2014-12-02 13:17:01" }, { "name": "phpunit/php-file-iterator", @@ -465,45 +525,44 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.2.2", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876", + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", @@ -511,55 +570,56 @@ "keywords": [ "tokenizer" ], - "time": "2014-03-03 05:10:30" + "time": "2014-08-31 06:12:13" }, { "name": "phpunit/phpunit", - "version": "3.7.37", + "version": "4.3.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc" + "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", - "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1", + "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.1", - "phpunit/php-timer": "~1.0", - "phpunit/phpunit-mock-objects": "~1.2", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.2", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.0", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.0", + "sebastian/exporter": "~1.0", + "sebastian/version": "~1.0", "symfony/yaml": "~2.0" }, - "require-dev": { - "pear-pear.php.net/pear": "1.9.4" - }, "suggest": { "phpunit/php-invoker": "~1.1" }, "bin": [ - "composer/bin/phpunit" + "phpunit" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.7.x-dev" + "dev-master": "4.3.x-dev" } }, "autoload": { "classmap": [ - "PHPUnit/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -584,39 +644,45 @@ "testing", "xunit" ], - "time": "2014-04-30 12:24:19" + "time": "2014-11-11 10:11:09" }, { "name": "phpunit/phpunit-mock-objects", - "version": "1.2.3", + "version": "2.3.0", "source": { "type": "git", - "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "1.2.3" + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "c63d2367247365f688544f0d500af90a11a44c65" }, "dist": { "type": "zip", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", - "reference": "1.2.3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", + "reference": "c63d2367247365f688544f0d500af90a11a44c65", "shasum": "" }, "require": { + "doctrine/instantiator": "~1.0,>=1.0.1", "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" }, "suggest": { "ext-soap": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, "autoload": { "classmap": [ - "PHPUnit/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -633,21 +699,287 @@ "mock", "xunit" ], - "time": "2013-01-13 10:24:48" + "time": "2014-10-03 05:12:11" + }, + { + "name": "sebastian/comparator", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", + "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.1", + "sebastian/exporter": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2014-05-11 23:00:21" + }, + { + "name": "sebastian/diff", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", + "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2014-08-15 10:29:00" + }, + { + "name": "sebastian/environment", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e6c71d918088c251b181ba8b3088af4ac336dd7", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2014-10-25 08:00:45" + }, + { + "name": "sebastian/exporter", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", + "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2014-09-10 00:51:36" + }, + { + "name": "sebastian/version", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2014-03-07 15:35:33" }, { "name": "symfony/yaml", - "version": "v2.5.4", + "version": "v2.6.0", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "01a7695bcfb013d0a15c6757e15aae120342986f" + "reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/01a7695bcfb013d0a15c6757e15aae120342986f", - "reference": "01a7695bcfb013d0a15c6757e15aae120342986f", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", + "reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", "shasum": "" }, "require": { @@ -656,7 +988,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } }, "autoload": { @@ -680,7 +1012,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-08-31 03:22:04" + "time": "2014-11-20 13:24:23" } ], "aliases": [], From 2b7b6058399907ec5e65911f03eae5e359695e0f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Dec 2014 13:06:24 +0000 Subject: [PATCH 506/638] Skip empty time values --- src/Composer/Repository/Vcs/GitBitbucketDriver.php | 2 +- src/Composer/Repository/Vcs/GitDriver.php | 2 +- src/Composer/Repository/Vcs/GitHubDriver.php | 2 +- src/Composer/Repository/Vcs/HgBitbucketDriver.php | 2 +- src/Composer/Repository/Vcs/HgDriver.php | 2 +- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 12eba6d3a..88a6d5454 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -93,7 +93,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer, $resource); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; $changeset = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $changeset['timestamp']; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index a47643988..50e191c96 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -156,7 +156,7 @@ class GitDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $resource); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 06b805714..9dbd21059 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -171,7 +171,7 @@ class GitHubDriver extends VcsDriver if ($composer) { $composer = JsonFile::parseJson($composer, $resource); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier); $commit = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $commit['commit']['committer']['date']; diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index c6eac73b9..5e6f71241 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -102,7 +102,7 @@ class HgBitbucketDriver extends VcsDriver $composer = JsonFile::parseJson($repoData['data'], $resource); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; $changeset = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $changeset['timestamp']; diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index eb962f276..470651acc 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -124,7 +124,7 @@ class HgDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $identifier); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime(trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index c8d9a17a6..c602ddaa3 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -148,7 +148,7 @@ class SvnDriver extends VcsDriver $composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev); - if (!isset($composer['time'])) { + if (empty($composer['time'])) { $output = $this->execute('svn info', $this->baseUrl . $path . $rev); foreach ($this->process->splitLines($output) as $line) { if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) { From fac3e135e3f0a9f84038b1048e0224e6f71b223e Mon Sep 17 00:00:00 2001 From: Douglas Greenshields Date: Wed, 3 Dec 2014 13:25:25 +0000 Subject: [PATCH 507/638] fix typo --- src/Composer/Command/DiagnoseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index ced252787..92ccf9291 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -255,7 +255,7 @@ EOT $latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false)); if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') { - return 'Your are not running the latest version'; + return 'You are not running the latest version'; } return true; From 9ca99762f9e41dec12be28b346f6fb71f72d85a5 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Wed, 3 Dec 2014 13:58:23 +0000 Subject: [PATCH 508/638] Prevent APC causing random fatal errors [fixes #264] Update the phar stub to prevent APC attempting to run the composer classes through the opcode cache on supported APC versions to avoid the random fatal "duplicate class" errors at runtime as reported in #264. Prior to APC version 3.0.12 this was a PHP_INI_SYSTEM directive, so output a warning instead. --- src/Composer/Compiler.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index b1b3725c6..e410c1878 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -212,6 +212,16 @@ class Compiler * the license that is located at the bottom of this file. */ +// Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264 +if (extension_loaded('apc') && ini_get('apc.enable_cli') && ini_get('apc.cache_by_default')) { + if (version_compare(phpversion('apc'), '3.0.12', '>=')) { + ini_set('apc.cache_by_default', 0); + } else { + fwrite(STDERR, 'Warning: APC <= 3.0.12 may cause fatal errors when running composer commands.'.PHP_EOL); + fwrite(STDERR, 'Update APC, or set apc.enable_cli or apc.cache_by_default to 0 in your php.ini.'.PHP_EOL); + } +} + Phar::mapPhar('composer.phar'); EOF; From 859c8f5bb3b8998e351c1dd52aab8d46e81cb222 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 4 Dec 2014 15:10:56 +0100 Subject: [PATCH 509/638] Use the repositories configured in composer.json for the require command --- src/Composer/Command/InitCommand.php | 3 ++- src/Composer/Command/RequireCommand.php | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 462b6d438..38dc2c4a5 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -33,8 +33,9 @@ use Symfony\Component\Process\ExecutableFinder; */ class InitCommand extends Command { + protected $repos; + private $gitConfig; - private $repos; private $pool; public function parseAuthorString($author) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index d0a63f1f3..f20c0420c 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -23,6 +23,8 @@ use Composer\Json\JsonManipulator; use Composer\Package\Version\VersionParser; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Repository\CompositeRepository; +use Composer\Repository\PlatformRepository; /** * @author Jérémy Romey @@ -78,14 +80,22 @@ EOT } $json = new JsonFile($file); - $composer = $json->read(); + $composerDefinition = $json->read(); $composerBackup = file_get_contents($json->getPath()); + $composer = $this->getComposer(); + $repos = $composer->getRepositoryManager()->getRepositories(); + + $this->repos = new CompositeRepository(array_merge( + array(new PlatformRepository), + $repos + )); + $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; - $baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); + $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); // validate requirements format @@ -98,13 +108,13 @@ EOT foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; - if (isset($composer[$removeKey][$package])) { - unset($composer[$removeKey][$package]); + if (isset($composerDefinition[$removeKey][$package])) { + unset($composerDefinition[$removeKey][$package]); } } - $composer[$requireKey] = $baseRequirements; - $json->write($composer); + $composerDefinition[$requireKey] = $baseRequirements; + $json->write($composerDefinition); } $output->writeln(''.$file.' has been '.($newlyCreated ? 'created' : 'updated').''); @@ -115,7 +125,6 @@ EOT $updateDevMode = !$input->getOption('update-no-dev'); // Update packages - $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); From d249d0c1218a06a9380a97edee3b7a6b9e42d714 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 5 Dec 2014 11:58:21 +0000 Subject: [PATCH 510/638] Reset composer to load the new requirements, fixes #3511 --- src/Composer/Command/Command.php | 9 +++++++++ src/Composer/Command/RequireCommand.php | 2 ++ src/Composer/Console/Application.php | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/src/Composer/Command/Command.php b/src/Composer/Command/Command.php index 862b54e58..b99686219 100644 --- a/src/Composer/Command/Command.php +++ b/src/Composer/Command/Command.php @@ -68,6 +68,15 @@ abstract class Command extends BaseCommand $this->composer = $composer; } + /** + * Removes the cached composer instance + */ + public function resetComposer() + { + $this->composer = null; + $this->getApplication()->resetComposer(); + } + /** * @return IOInterface */ diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index f20c0420c..55e3752a9 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -125,6 +125,8 @@ EOT $updateDevMode = !$input->getOption('update-no-dev'); // Update packages + $this->resetComposer(); + $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index e0037f655..6ce2d6682 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -219,6 +219,14 @@ class Application extends BaseApplication return $this->composer; } + /** + * Removes the cached composer instance + */ + public function resetComposer() + { + $this->composer = null; + } + /** * @return IOInterface */ From 59648b12a465ed91b6bece57f38edfab2458f29f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Dec 2014 13:47:34 +0000 Subject: [PATCH 511/638] Add HHVM support in UA header --- src/Composer/Util/RemoteFilesystem.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 93f970db2..cfb4a946e 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -385,15 +385,19 @@ class RemoteFilesystem protected function getOptionsForUrl($originUrl, $additionalOptions) { + if (defined('HHVM_VERSION')) { + $phpVersion = 'HHVM ' . HHVM_VERSION; + } else { + $phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; + } + $headers = array( sprintf( - 'User-Agent: Composer/%s (%s; %s; PHP %s.%s.%s)', + 'User-Agent: Composer/%s (%s; %s; %s)', Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION, php_uname('s'), php_uname('r'), - PHP_MAJOR_VERSION, - PHP_MINOR_VERSION, - PHP_RELEASE_VERSION + $phpVersion ) ); From fc3c7838b2ada9ee7a376a56d2eb07b405d98fd3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Dec 2014 13:54:29 +0000 Subject: [PATCH 512/638] CS fixes --- src/Composer/Autoload/ClassLoader.php | 1 + src/Composer/Config.php | 2 +- src/Composer/Console/Application.php | 3 ++- src/Composer/Package/BasePackage.php | 1 - src/Composer/Package/CompletePackage.php | 2 +- tests/Composer/Test/AllFunctionalTest.php | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 4a56396af..70d78bc3f 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -59,6 +59,7 @@ class ClassLoader if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', $this->prefixesPsr0); } + return array(); } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index bd5d86895..176c228eb 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -300,7 +300,7 @@ class Config * This should be used to read COMPOSER_ environment variables * that overload config values. * - * @param string $var + * @param string $var * @return string|boolean */ private function getComposerEnv($var) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 6ce2d6682..940864683 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -99,7 +99,8 @@ class Application extends BaseApplication if ($name = $this->getCommandName($input)) { try { $commandName = $this->find($name)->getName(); - } catch (\InvalidArgumentException $e) {} + } catch (\InvalidArgumentException $e) { + } } if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { if (time() > COMPOSER_DEV_WARNING_TIME) { diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 677d5074d..965e5ddc8 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -56,7 +56,6 @@ abstract class BasePackage implements PackageInterface protected $repository; protected $transportOptions; - /** * All descendants' constructors should call this parent constructor * diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index 27c9abeca..0de1609d0 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -194,6 +194,6 @@ class CompletePackage extends Package implements CompletePackageInterface */ public function getReplacementPackage() { - return is_string($this->abandoned)? $this->abandoned : null; + return is_string($this->abandoned) ? $this->abandoned : null; } } diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index 39f0c23d6..4a23c5717 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -63,7 +63,7 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase if (defined('HHVM_VERSION')) { $this->markTestSkipped('Building the phar does not work on HHVM.'); } - + $fs = new Filesystem; $fs->removeDirectory(dirname(self::$pharPath)); $fs->ensureDirectoryExists(dirname(self::$pharPath)); From 46d8e50bf373bbfe5d3274b26dc15e84644b7f91 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Dec 2014 14:02:17 +0000 Subject: [PATCH 513/638] Fix test on windows --- tests/Composer/Test/InstallerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 9983b8c47..4dc0bf511 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -292,7 +292,7 @@ class InstallerTest extends TestCase // Change paths like file://foobar to file:///path/to/fixtures if (preg_match('{^file://[^/]}', $repo['url'])) { - $repo['url'] = "file://${fixturesDir}/" . substr($repo['url'], 7); + $repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7); } unset($repo); From eb1ce550ca51134ee619ad3e37f5a0b7e980dd24 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Dec 2014 17:15:20 +0000 Subject: [PATCH 514/638] Update changelog for alpha9, fixes #3029 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e152f1ddc..d78b8e109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +### 1.0.0-alpha9 (2014-12-07) + + * Added `remove` command to do the reverse of `require` + * Added --ignore-platform-reqs to `install`/`update` commands to install even if you are missing a php extension or have an invalid php version + * Added a warning when abandoned packages are being installed + * Added auto-selection of the version constraint in the `require` command, which can now be used simply as `composer require foo/bar` + * Added ability to define custom composer commands using scripts + * Added `browse` command to open a browser to the given package's repo URL (or homepage with `-H`) + * Added an `autoload-dev` section to declare dev-only autoload rules + a --no-dev flag to dump-autoload + * Added an `auth.json` file, with `store-auths` config option + * Added a `http-basic` config option to store login/pwds to hosts + * Added failover to source/dist and vice-versa in case a download method fails + * Added --path (-P) flag to the show command to see the install path of packages + * Added --update-with-dependencies and --update-no-dev flags to the require command + * Added `optimize-autoloader` config option to force the `-o` flag from the config + * Added `clear-cache` command + * Added a GzipDownloader to download single gzipped files + * Added `ssh` support in the `github-protocols` config option + * Added `pre-dependencies-solving` and `post-dependencies-solving` events + * Added `pre-archive-cmd` and `post-archive-cmd` script events to the `archive` command + * Added a `no-api` flag to GitHub VCS repos to skip the API but still get zip downloads + * Added http-basic auth support for private git repos not on github + * Added support for autoloading `.hh` files when running HHVM + * Added support for PHP 5.6 + * Added support for OTP auth when retrieving a GitHub API key + * Fixed isolation of `files` autoloaded scripts to ensure they can not affect anything + * Improved performance of solving dependencies + * Improved SVN and Perforce support + * A boatload of minor fixes, documentation additions and UX improvements + ### 1.0.0-alpha8 (2014-01-06) * Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements From 029f7093009f621bd20aef98c7bfc61631f18cf1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 7 Dec 2014 17:46:04 +0000 Subject: [PATCH 515/638] Fix parsing stability suffixes in multi constraints, fixes #1732 --- src/Composer/Package/Version/VersionParser.php | 2 +- .../Test/Package/Version/VersionParserTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index f14f09622..a141bdc43 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -360,7 +360,7 @@ class VersionParser if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') { $version .= '-' . $stabilityModifier; } elseif ('<' === $matches[1]) { - if (!preg_match('/-stable$/', strtolower($matches[2]))) { + if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) { $version .= '-dev'; } } diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 25c2b9898..c8136f6f3 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -300,6 +300,20 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame((string) $multi, (string) $parser->parseConstraints('>2.0,<=3.0')); } + public function testParseConstraintsMultiWithStabilitySuffix() + { + $parser = new VersionParser; + $first = new VersionConstraint('>=', '1.1.0.0-alpha4'); + $second = new VersionConstraint('<', '1.2.9999999.9999999-dev'); + $multi = new MultiConstraint(array($first, $second)); + $this->assertSame((string) $multi, (string) $parser->parseConstraints('>=1.1.0-alpha4,<1.2.x-dev')); + + $first = new VersionConstraint('>=', '1.1.0.0-alpha4'); + $second = new VersionConstraint('<', '1.2.0.0-beta2'); + $multi = new MultiConstraint(array($first, $second)); + $this->assertSame((string) $multi, (string) $parser->parseConstraints('>=1.1.0-alpha4,<1.2-beta2')); + } + public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive() { $parser = new VersionParser; From ca168d478bd7a52491e5f3f1384ee4ee0b1a5fe5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 15 Jan 2014 16:47:27 +0100 Subject: [PATCH 516/638] Spaces are now equivalent to comma in constraints and mean AND --- doc/01-basic-usage.md | 2 +- doc/04-schema.md | 4 ++-- .../Package/Version/VersionParser.php | 2 +- .../Package/Version/VersionParserTest.php | 20 +++++++++++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 06d2a8c5d..abf684488 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a comma (`,`) will be treated as a **logical AND**. A pipe (|) will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A pipe (|) will be treated as a **logical OR**. AND has higher precedence than OR. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. diff --git a/doc/04-schema.md b/doc/04-schema.md index c768edb72..9799b4a5c 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -345,10 +345,10 @@ dependencies from being installed. Lists packages that conflict with this version of this package. They will not be allowed to be installed together with your package. -Note that when specifying ranges like `<1.0, >= 1.1` in a `conflict` link, +Note that when specifying ranges like `<1.0 >=1.1` in a `conflict` link, this will state a conflict with all versions that are less than 1.0 *and* equal or newer than 1.1 at the same time, which is probably not what you want. You -probably want to go for `<1.0 | >= 1.1` in this case. +probably want to go for `<1.0 | >=1.1` in this case. #### replace diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index a141bdc43..a2ad3bd11 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -233,7 +233,7 @@ class VersionParser $orConstraints = preg_split('{\s*\|\s*}', trim($constraints)); $orGroups = array(); foreach ($orConstraints as $constraints) { - $andConstraints = preg_split('{\s*,\s*}', $constraints); + $andConstraints = preg_split('{(?<])\s*[, ]+\s*(?!as)}', $constraints); if (count($andConstraints) > 1) { $constraintObjects = array(); diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index c8136f6f3..5a15dd714 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -221,6 +221,8 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')), 'shorthand beta' => array('1.2.3b5', new VersionConstraint('=', '1.2.3.0-beta5')), 'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')), + 'accepts spaces/2' => array('< 1.2.3', new VersionConstraint('<', '1.2.3.0-dev')), + 'accepts spaces/3' => array('> 1.2.3', new VersionConstraint('>', '1.2.3.0')), 'accepts master' => array('>=dev-master', new VersionConstraint('>=', '9999999-dev')), 'accepts master/2' => array('dev-master', new VersionConstraint('=', '9999999-dev')), 'accepts arbitrary' => array('dev-feature-a', new VersionConstraint('=', 'dev-feature-a')), @@ -291,13 +293,27 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase ); } - public function testParseConstraintsMulti() + /** + * @dataProvider multiConstraintProvider + */ + public function testParseConstraintsMulti($constraint) { $parser = new VersionParser; $first = new VersionConstraint('>', '2.0.0.0'); $second = new VersionConstraint('<=', '3.0.0.0'); $multi = new MultiConstraint(array($first, $second)); - $this->assertSame((string) $multi, (string) $parser->parseConstraints('>2.0,<=3.0')); + $this->assertSame((string) $multi, (string) $parser->parseConstraints($constraint)); + } + + public function multiConstraintProvider() + { + return array( + array('>2.0,<=3.0'), + array('>2.0 <=3.0'), + array('>2.0, <=3.0'), + array('>2.0 ,<=3.0'), + array('>2.0 , <=3.0'), + ); } public function testParseConstraintsMultiWithStabilitySuffix() From 4c713978e9dd15290cb4432fdbd176389d0af682 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 15 Jan 2014 17:13:42 +0100 Subject: [PATCH 517/638] Add more tests and validate that there is only one comma --- src/Composer/Package/Version/VersionParser.php | 2 +- tests/Composer/Test/Package/Version/VersionParserTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index a2ad3bd11..eb1529648 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -233,7 +233,7 @@ class VersionParser $orConstraints = preg_split('{\s*\|\s*}', trim($constraints)); $orGroups = array(); foreach ($orConstraints as $constraints) { - $andConstraints = preg_split('{(?<])\s*[, ]+\s*(?!as)}', $constraints); + $andConstraints = preg_split('{(?< ,]) *[, ] *(?!,|as|$)}', $constraints); if (count($andConstraints) > 1) { $constraintObjects = array(); diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 5a15dd714..a312b2e98 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -313,6 +313,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase array('>2.0, <=3.0'), array('>2.0 ,<=3.0'), array('>2.0 , <=3.0'), + array('>2.0 , <=3.0'), + array('> 2.0 <= 3.0'), + array('> 2.0 , <= 3.0'), + array(' > 2.0 , <= 3.0 '), ); } @@ -365,6 +369,9 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase return array( 'empty ' => array(''), 'invalid version' => array('1.0.0-meh'), + 'operator abuse' => array('>2.0,,<=3.0'), + 'operator abuse/2' => array('>2.0 ,, <=3.0'), + 'operator abuse/3' => array('>2.0 || <=3.0'), ); } From 69ef80124f4033f0b995c8e84dfbff5a92b40406 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 12:57:51 +0000 Subject: [PATCH 518/638] Add support for capital X in 3.X and || for OR --- doc/01-basic-usage.md | 2 +- .../Package/Version/VersionParser.php | 10 ++++---- .../Package/Version/VersionParserTest.php | 24 +++++++++++++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index abf684488..493c42cd4 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A pipe (|) will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A pipe (|) or double pipe will be treated as a **logical OR**. AND has higher precedence than OR. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index eb1529648..9b5df66b0 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -178,10 +178,10 @@ class VersionParser return $this->normalize($name); } - if (preg_match('#^v?(\d+)(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?$#i', $name, $matches)) { + if (preg_match('#^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$#i', $name, $matches)) { $version = ''; for ($i = 1; $i < 5; $i++) { - $version .= isset($matches[$i]) ? str_replace('*', 'x', $matches[$i]) : '.x'; + $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x'; } return str_replace('x', '9999999', $version).'-dev'; @@ -230,7 +230,7 @@ class VersionParser $constraints = $match[1]; } - $orConstraints = preg_split('{\s*\|\s*}', trim($constraints)); + $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints)); $orGroups = array(); foreach ($orConstraints as $constraints) { $andConstraints = preg_split('{(?< ,]) *[, ] *(?!,|as|$)}', $constraints); @@ -273,7 +273,7 @@ class VersionParser } } - if (preg_match('{^[x*](\.[x*])*$}i', $constraint)) { + if (preg_match('{^[xX*](\.[xX*])*$}i', $constraint)) { return array(new EmptyConstraint); } @@ -330,7 +330,7 @@ class VersionParser } // match wildcard constraints - if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$}', $constraint, $matches)) { + if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[xX*]$}', $constraint, $matches)) { if (isset($matches[3]) && '' !== $matches[3]) { $position = 3; } elseif (isset($matches[2]) && '' !== $matches[2]) { diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index a312b2e98..2ed19c416 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -90,6 +90,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'forces w.x.y.z/2' => array('0', '0.0.0.0'), 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), 'parses long/2' => array('10.4.13beta2', '10.4.13.0-beta2'), + 'parses long/semver' => array('10.4.13beta.2', '10.4.13.0-beta2'), 'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'), 'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'), 'strips leading v' => array('v1.0.0', '1.0.0.0'), @@ -208,7 +209,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'match any' => array('*', new EmptyConstraint()), 'match any/2' => array('*.*', new EmptyConstraint()), 'match any/3' => array('*.x.*', new EmptyConstraint()), - 'match any/4' => array('x.x.x.*', new EmptyConstraint()), + 'match any/4' => array('x.X.x.*', new EmptyConstraint()), 'not equal' => array('<>1.0.0', new VersionConstraint('<>', '1.0.0.0')), 'not equal/2' => array('!=1.0.0', new VersionConstraint('!=', '1.0.0.0')), 'greater than' => array('>1.0.0', new VersionConstraint('>', '1.0.0.0')), @@ -255,7 +256,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase array('20.*', new VersionConstraint('>=', '20.0.0.0-dev'), new VersionConstraint('<', '21.0.0.0-dev')), array('2.0.*', new VersionConstraint('>=', '2.0.0.0-dev'), new VersionConstraint('<', '2.1.0.0-dev')), array('2.2.x', new VersionConstraint('>=', '2.2.0.0-dev'), new VersionConstraint('<', '2.3.0.0-dev')), - array('2.10.x', new VersionConstraint('>=', '2.10.0.0-dev'), new VersionConstraint('<', '2.11.0.0-dev')), + array('2.10.X', new VersionConstraint('>=', '2.10.0.0-dev'), new VersionConstraint('<', '2.11.0.0-dev')), array('2.1.3.*', new VersionConstraint('>=', '2.1.3.0-dev'), new VersionConstraint('<', '2.1.4.0-dev')), array('0.*', null, new VersionConstraint('<', '1.0.0.0-dev')), ); @@ -310,6 +311,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase return array( array('>2.0,<=3.0'), array('>2.0 <=3.0'), + array('>2.0 <=3.0'), array('>2.0, <=3.0'), array('>2.0 ,<=3.0'), array('>2.0 , <=3.0'), @@ -334,7 +336,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame((string) $multi, (string) $parser->parseConstraints('>=1.1.0-alpha4,<1.2-beta2')); } - public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive() + /** + * @dataProvider multiConstraintProvider2 + */ + public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive($constraint) { $parser = new VersionParser; $first = new VersionConstraint('>', '2.0.0.0'); @@ -342,7 +347,16 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $third = new VersionConstraint('>', '2.0.6.0'); $multi1 = new MultiConstraint(array($first, $second)); $multi2 = new MultiConstraint(array($multi1, $third), false); - $this->assertSame((string) $multi2, (string) $parser->parseConstraints('>2.0,<2.0.5 | >2.0.6')); + $this->assertSame((string) $multi2, (string) $parser->parseConstraints($constraint)); + } + + public function multiConstraintProvider2() + { + return array( + array('>2.0,<2.0.5 | >2.0.6'), + array('>2.0,<2.0.5 || >2.0.6'), + array('> 2.0 , <2.0.5 | > 2.0.6'), + ); } public function testParseConstraintsMultiWithStabilities() @@ -371,7 +385,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'invalid version' => array('1.0.0-meh'), 'operator abuse' => array('>2.0,,<=3.0'), 'operator abuse/2' => array('>2.0 ,, <=3.0'), - 'operator abuse/3' => array('>2.0 || <=3.0'), + 'operator abuse/3' => array('>2.0 ||| <=3.0'), ); } From ebab9db27ac3887babc8404107dd448d974b2f43 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 13:44:42 +0000 Subject: [PATCH 519/638] Add support for hyphen ranges --- doc/01-basic-usage.md | 3 +- .../Package/Version/VersionParser.php | 34 +++++++++++++++++-- .../Package/Version/VersionParserTest.php | 27 +++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 493c42cd4..d3a130ff3 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,8 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0,<2.0` >=1.0,<1.1 | >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A pipe (|) or double pipe will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0 <2.0` >=1.0 <1.1 || >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A double pipe (||) will be treated as a **logical OR**. AND has higher precedence than OR. +Hyphen Range | `1.0 - 2.0` | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 9b5df66b0..c10e85939 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -233,8 +233,7 @@ class VersionParser $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints)); $orGroups = array(); foreach ($orConstraints as $constraints) { - $andConstraints = preg_split('{(?< ,]) *[, ] *(?!,|as|$)}', $constraints); - + $andConstraints = preg_split('{(?< ,]) *(? 1) { $constraintObjects = array(); foreach ($andConstraints as $constraint) { @@ -277,12 +276,14 @@ class VersionParser return array(new EmptyConstraint); } + $versionRegex = '(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex; + // match tilde constraints // like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous // version, to ensure that unstable instances of the current version are allowed. // however, if a stability suffix is added to the constraint, then a >= match on the current version is // used instead - if (preg_match('{^~>?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex.'?$}i', $constraint, $matches)) { + if (preg_match('{^~>?'.$versionRegex.'$}i', $constraint, $matches)) { if (substr($constraint, 0, 2) === '~>') { throw new \UnexpectedValueException( 'Could not parse version constraint '.$constraint.': '. @@ -352,6 +353,33 @@ class VersionParser ); } + // match hyphen constraints + if (preg_match('{^(?P'.$versionRegex.') +- +(?P'.$versionRegex.')($)}i', $constraint, $matches)) { + // Calculate the stability suffix + $lowStabilitySuffix = ''; + if (empty($matches[6]) && empty($matches[8])) { + $lowStabilitySuffix = '-dev'; + } + + $lowVersion = $this->normalize($matches['from']); + $lowerBound = new VersionConstraint('>=', $lowVersion . $lowStabilitySuffix); + + $highVersion = $matches[10]; + if ((!empty($matches[11]) && !empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) { + $highVersion = $this->normalize($matches['to']); + $upperBound = new VersionConstraint('<=', $highVersion); + } else { + $highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]); + $highVersion = $this->manipulateVersionString($highMatch, empty($matches[11]) ? 1 : 2, 1) . '-dev'; + $upperBound = new VersionConstraint('<', $highVersion); + } + + return array( + $lowerBound, + $upperBound + ); + } + // match operators constraints if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) { try { diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 2ed19c416..58b4c9e36 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -294,6 +294,33 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase ); } + /** + * @dataProvider hyphenConstraints + */ + public function testParseHyphen($input, $min, $max) + { + $parser = new VersionParser; + if ($min) { + $expected = new MultiConstraint(array($min, $max)); + } else { + $expected = $max; + } + + $this->assertSame((string) $expected, (string) $parser->parseConstraints($input)); + } + + public function hyphenConstraints() + { + return array( + array('1 - 2', new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '3.0.0.0-dev')), + array('1.2.3 - 2.3.4.5', new VersionConstraint('>=', '1.2.3.0-dev'), new VersionConstraint('<=', '2.3.4.5')), + array('1.2-beta - 2.3', new VersionConstraint('>=', '1.2.0.0-beta'), new VersionConstraint('<', '2.4.0.0-dev')), + array('1.2-beta - 2.3-dev', new VersionConstraint('>=', '1.2.0.0-beta'), new VersionConstraint('<=', '2.3.0.0-dev')), + array('1.2-RC - 2.3.1', new VersionConstraint('>=', '1.2.0.0-RC'), new VersionConstraint('<=', '2.3.1.0')), + array('1.2.3-alpha - 2.3-RC', new VersionConstraint('>=', '1.2.3.0-alpha'), new VersionConstraint('<=', '2.3.0.0-RC')), + ); + } + /** * @dataProvider multiConstraintProvider */ From f363f9d3701c9a5f4255e15e63c26c54776a0404 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 15:39:52 +0000 Subject: [PATCH 520/638] Add support for caret (^) operator --- doc/01-basic-usage.md | 23 ++++++++----- .../Package/Version/VersionParser.php | 31 +++++++++++++++++ .../Package/Version/VersionParserTest.php | 33 +++++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index d3a130ff3..bebac5355 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -72,18 +72,19 @@ means any version in the `1.0` development branch. It would match `1.0.0`, Version constraints can be specified in a few different ways. -Name | Example | Description --------------- | ------------------------------------------------------------------ | ----------- -Exact version | `1.0.2` | You can specify the exact version of a package. +Name | Example | Description +-------------- | ------------------------------------------------------------------------ | ----------- +Exact version | `1.0.2` | You can specify the exact version of a package. Range | `>=1.0` `>=1.0 <2.0` >=1.0 <1.1 || >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A double pipe (||) will be treated as a **logical OR**. AND has higher precedence than OR. -Hyphen Range | `1.0 - 2.0` | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`. -Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. -Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below. +Hyphen Range | `1.0 - 2.0` | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`. +Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`. +Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2 <2.0`. For more details, read the next section below. +Caret Operator | `^1.2.3` | Very useful for projects that follow semantic versioning. `^1.2.3` is equivalent to `>=1.2.3 <2.0`. For more details, read the next section below. -### Next Significant Release (Tilde Operator) +### Next Significant Release (Tilde and Caret Operators) The `~` operator is best explained by example: `~1.2` is equivalent to -`>=1.2,<2.0`, while `~1.2.3` is equivalent to `>=1.2.3,<1.3`. As you can see +`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see it is mostly useful for projects respecting [semantic versioning](http://semver.org/). A common usage would be to mark the minimum minor version you depend on, like `~1.2` (which allows anything up to, but not @@ -91,6 +92,12 @@ including, 2.0). Since in theory there should be no backwards compatibility breaks until 2.0, that works well. Another way of looking at it is that using `~` specifies a minimum version, but allows the last digit specified to go up. +The `^` operator behaves very similarly but it sticks closer to semantic +versioning, and will always allow non-breaking updates. For example `^1.2.3` +is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should +break backwards compatibility. For pre-1.0 versions it also acts with safety +in mind and treats `^0.3` as `>=0.3.0 <0.4.0` + > **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint > like `~1.2` would not install it. As said above `~1.2` only means the `.2` > can change but the `1.` part is fixed. diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index c10e85939..70c3de0e5 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -330,6 +330,37 @@ class VersionParser ); } + // match caret constraints + if (preg_match('{^\^'.$versionRegex.'($)}i', $constraint, $matches)) { + // Work out which position in the version we are operating at + if ('0' !== $matches[1] || '' === $matches[2]) { + $position = 1; + } elseif ('0' !== $matches[2] || '' === $matches[3]) { + $position = 2; + } else { + $position = 3; + } + + // Calculate the stability suffix + $stabilitySuffix = ''; + if (empty($matches[5]) && empty($matches[7])) { + $stabilitySuffix .= '-dev'; + } + + $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1)); + $lowerBound = new VersionConstraint('>=', $lowVersion); + + // For upper bound, we increment the position of one more significance, + // but highPosition = 0 would be illegal + $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev'; + $upperBound = new VersionConstraint('<', $highVersion); + + return array( + $lowerBound, + $upperBound + ); + } + // match wildcard constraints if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[xX*]$}', $constraint, $matches)) { if (isset($matches[3]) && '' !== $matches[3]) { diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 58b4c9e36..15db1ef69 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -294,6 +294,39 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase ); } + /** + * @dataProvider caretConstraints + */ + public function testParseCaretWildcard($input, $min, $max) + { + $parser = new VersionParser; + if ($min) { + $expected = new MultiConstraint(array($min, $max)); + } else { + $expected = $max; + } + + $this->assertSame((string) $expected, (string) $parser->parseConstraints($input)); + } + + public function caretConstraints() + { + return array( + array('^1', new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')), + array('^0', new VersionConstraint('>=', '0.0.0.0-dev'), new VersionConstraint('<', '1.0.0.0-dev')), + array('^0.0', new VersionConstraint('>=', '0.0.0.0-dev'), new VersionConstraint('<', '0.1.0.0-dev')), + array('^1.2', new VersionConstraint('>=', '1.2.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')), + array('^1.2.3-beta.2', new VersionConstraint('>=', '1.2.3.0-beta2'), new VersionConstraint('<', '2.0.0.0-dev')), + array('^1.2.3.4', new VersionConstraint('>=', '1.2.3.4-dev'), new VersionConstraint('<', '2.0.0.0-dev')), + array('^1.2.3', new VersionConstraint('>=', '1.2.3.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')), + array('^0.2.3', new VersionConstraint('>=', '0.2.3.0-dev'), new VersionConstraint('<', '0.3.0.0-dev')), + array('^0.2', new VersionConstraint('>=', '0.2.0.0-dev'), new VersionConstraint('<', '0.3.0.0-dev')), + array('^0.0.3', new VersionConstraint('>=', '0.0.3.0-dev'), new VersionConstraint('<', '0.0.4.0-dev')), + array('^0.0.3-alpha', new VersionConstraint('>=', '0.0.3.0-alpha'), new VersionConstraint('<', '0.0.4.0-dev')), + array('^0.0.3-dev', new VersionConstraint('>=', '0.0.3.0-dev'), new VersionConstraint('<', '0.0.4.0-dev')), + ); + } + /** * @dataProvider hyphenConstraints */ From 92f4c1fb35a12afd69d98f810ee083a66ba48bff Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 15:52:32 +0000 Subject: [PATCH 521/638] Allow and ignore semver build metadata, fixes #2422 --- src/Composer/Package/Version/VersionParser.php | 5 +++++ tests/Composer/Test/Package/Version/VersionParserTest.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 70c3de0e5..2a2c38371 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -103,6 +103,11 @@ class VersionParser $version = $match[1]; } + // ignore build metadata + if (preg_match('{^([^,\s+]+)\+[^\s]+$}', $version, $match)) { + $version = $match[1]; + } + // match master-like branches if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { return '9999999-dev'; diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 15db1ef69..88058f6aa 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -110,6 +110,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-FOOBAR'), 'parses arbitrary3' => array('dev-feature/foo', 'dev-feature/foo'), 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), + 'semver metadata' => array('dev-master+foo.bar', '9999999-dev'), + 'semver metadata/2' => array('1.0.0-beta.5+foo', '1.0.0.0-beta5'), + 'semver metadata/3' => array('1.0.0+foo', '1.0.0.0'), + 'metadata w/ alias' => array('1.0.0+foo as 2.0', '1.0.0.0'), ); } @@ -131,6 +135,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'invalid type' => array('1.0.0-meh'), 'too many bits' => array('1.0.0.0.0'), 'non-dev arbitrary' => array('feature-foo'), + 'metadata w/ space' => array('1.0.0+foo bar'), ); } From 9438f3a58f5789016fe121e23b92ef09a4eb37ae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 16:57:27 +0000 Subject: [PATCH 522/638] Use better semver ranges for 0.x versions, fixes #3518 --- src/Composer/Package/Version/VersionSelector.php | 16 ++++++++++++++-- .../Test/Package/Version/VersionSelectorTest.php | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 202b11883..ce416ca12 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -103,10 +103,22 @@ class VersionSelector // attempt to transform 2.1.1 to 2.1 // this allows you to upgrade through minor versions $semanticVersionParts = explode('.', $version); + $op = '~'; + // check to see if we have a semver-looking version if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) { // remove the last parts (i.e. the patch version number and any extra) - unset($semanticVersionParts[2], $semanticVersionParts[3]); + if ($semanticVersionParts[0] === '0') { + if ($semanticVersionParts[2] === '0') { + $semanticVersionParts[2] = '*'; + unset($semanticVersionParts[3]); + } else { + $semanticVersionParts[3] = '*'; + } + $op = ''; + } else { + unset($semanticVersionParts[2], $semanticVersionParts[3]); + } $version = implode('.', $semanticVersionParts); } else { return $prettyVersion; @@ -118,7 +130,7 @@ class VersionSelector } // 2.1 -> ~2.1 - return '~'.$version; + return $op.$version; } private function getParser() diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 2a9cb45a0..884337396 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -98,7 +98,10 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase array('v1.2.1', false, 'stable', '~1.2'), array('3.1.2-pl2', false, 'stable', '~3.1'), array('3.1.2-patch', false, 'stable', '~3.1'), - // for non-stable versions, we add ~, but don't try the (1.2.1 -> 1.2) transformation + array('0.1.0', false, 'stable', '0.1.*'), + array('0.1.3', false, 'stable', '0.1.3.*'), + array('0.0.3', false, 'stable', '0.0.3.*'), + array('0.0.3-alpha', false, 'alpha', '0.0.3.*@alpha'), array('2.0-beta.1', false, 'beta', '~2.0@beta'), array('3.1.2-alpha5', false, 'alpha', '~3.1@alpha'), array('3.0-RC2', false, 'RC', '~3.0@RC'), From 06e96a78e2cc7c4ba354b0e9c3be7f334c07f82c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 17:04:59 +0000 Subject: [PATCH 523/638] Update ranges for 0.N versions, refs #3518 --- src/Composer/Package/Version/VersionSelector.php | 6 +++--- tests/Composer/Test/Package/Version/VersionSelectorTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index ce416ca12..b26f8915f 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -109,11 +109,11 @@ class VersionSelector if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) { // remove the last parts (i.e. the patch version number and any extra) if ($semanticVersionParts[0] === '0') { - if ($semanticVersionParts[2] === '0') { + if ($semanticVersionParts[1] === '0') { + $semanticVersionParts[3] = '*'; + } else { $semanticVersionParts[2] = '*'; unset($semanticVersionParts[3]); - } else { - $semanticVersionParts[3] = '*'; } $op = ''; } else { diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 884337396..90f820e8f 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -99,7 +99,7 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase array('3.1.2-pl2', false, 'stable', '~3.1'), array('3.1.2-patch', false, 'stable', '~3.1'), array('0.1.0', false, 'stable', '0.1.*'), - array('0.1.3', false, 'stable', '0.1.3.*'), + array('0.1.3', false, 'stable', '0.1.*'), array('0.0.3', false, 'stable', '0.0.3.*'), array('0.0.3-alpha', false, 'alpha', '0.0.3.*@alpha'), array('2.0-beta.1', false, 'beta', '~2.0@beta'), From d8f043e12c9938eeaad54fefca78a46ef1ce5198 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 21:19:55 +0000 Subject: [PATCH 524/638] Remove object state from methods to simplify things, refs #3385 --- src/Composer/Command/LicensesCommand.php | 36 ++++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index 321d17709..8ab9d94b4 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -28,12 +28,6 @@ use Symfony\Component\Console\Output\OutputInterface; */ class LicensesCommand extends Command { - /** - * @var list of packages to use - */ - - private $packageList = array(); - protected function configure() { $this @@ -65,12 +59,12 @@ EOT $versionParser = new VersionParser; if ($input->getOption('no-dev')) { - $this->findRequiresPackages($repo, $root); + $packages = $this->filterRequiredPackages($repo, $root); } else { - $this->addToPackageList($repo->getPackages()); + $packages = $this->appendPackages($repo->getPackages(), array()); } - ksort($this->packageList); + ksort($packages); switch ($format = $input->getOption('format')) { case 'text': @@ -82,7 +76,7 @@ EOT $table = $this->getHelperSet()->get('table'); $table->setLayout(TableHelper::LAYOUT_BORDERLESS); $table->setHorizontalBorderChar(''); - foreach ($this->packageList as $package) { + foreach ($packages as $package) { $table->addRow(array( $package->getPrettyName(), $versionParser->formatVersion($package), @@ -93,7 +87,7 @@ EOT break; case 'json': - foreach ($this->packageList as $package) { + foreach ($packages as $package) { $dependencies[$package->getPrettyName()] = array( 'version' => $versionParser->formatVersion($package), 'license' => $package->getLicense(), @@ -119,11 +113,11 @@ EOT * @param RepositoryInterface $repo * @param PackageInterface $package */ - private function findRequiresPackages(RepositoryInterface $repo, PackageInterface $package) + private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array()) { $requires = array_keys($package->getRequires()); - $packageListNames = array_keys($this->packageList); + $packageListNames = array_keys($bucket); $packages = array_filter( $repo->getPackages(), function ($package) use ($requires, $packageListNames) { @@ -131,22 +125,28 @@ EOT } ); - $this->addToPackageList($packages); + $bucket = $this->appendPackages($packages, $bucket); foreach ($packages as $package) { - $this->findRequiresPackages($repo, $package); + $bucket = $this->filterRequiredPackages($repo, $package, $bucket); } + + return $bucket; } /** * Adds packages to the package list * - * @param array $packages the list of packages to add + * @param array $packages the list of packages to add + * @param array $bucket the list to add packages to + * @return array */ - public function addToPackageList($packages) + public function appendPackages(array $packages, array $bucket) { foreach ($packages as $package) { - $this->packageList[$package->getName()] = $package; + $bucket[$package->getName()] = $package; } + + return $bucket; } } From 90a69cd02e1c437bdfbab073f6bc4f709dacee4f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 21:21:33 +0000 Subject: [PATCH 525/638] Add new arg to docs, refs #3385 --- doc/03-cli.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7fcafc2d6..69f8f58c5 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -487,6 +487,11 @@ performance. Lists the name, version and license of every package installed. Use `--format=json` to get machine readable output. +### Options + +* **--no-dev:** Remove dev dependencies from the output +* **--format:** Format of the output: text or json (default: "text") + ## run-script To run [scripts](articles/scripts.md) manually you can use this command, From fa470be0019e99373cff6cf76ff479e1507eb766 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 21:30:39 +0000 Subject: [PATCH 526/638] Wording tweaks and docblock formatting, refs #2536 --- doc/articles/http-basic-authentication.md | 70 +++++++++++++---------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/doc/articles/http-basic-authentication.md b/doc/articles/http-basic-authentication.md index c3d09fc34..af62fe2df 100644 --- a/doc/articles/http-basic-authentication.md +++ b/doc/articles/http-basic-authentication.md @@ -1,51 +1,59 @@ -# Http basic authentication +# HTTP basic authentication -Your [satis](handling-private-packages-with-satis.md) server could be -secured with http basic authentication. In order to allow your project +Your [Satis or Toran Proxy](handling-private-packages-with-satis.md) server +could be secured with http basic authentication. In order to allow your project to have access to these packages you will have to tell composer how to authenticate with your credentials. -The most simple way to provide your credentials is providing your set +The simplest way to provide your credentials is providing your set of credentials inline with the repository specification such as: - { - "repositories": [ - { - "type": "composer", - "url": "http://extremely:secret@repo.example.org" - } - ] - } +```json +{ + "repositories": [ + { + "type": "composer", + "url": "http://extremely:secret@repo.example.org" + } + ] +} +``` This will basically teach composer how to authenticate automatically when reading packages from the provided composer repository. This does not work for everybody especially when you don't want to hard code your credentials into your composer.json. There is a second -way to provide these details and is via interaction. If you don't +way to provide these details and it is via interaction. If you don't provide the authentication credentials composer will prompt you upon connection to enter the username and password. -There is yet another way to provide these details and is via a file -`auth.json` inside your `COMPOSER_HOME` which looks something like -`/Users/username/.composer/auth.json` +The third way if you want to pre-configure it is via an `auth.json` file +located in your `COMPOSER_HOME` or besides your `composer.json`. - { - "basic-auth": [ - "repo.example1.org": { - "username": "my-username1", - "password": "my-secret-password1" - }, - "repo.example2.org": { - "username": "my-username2", - "password": "my-secret-password2" - } - ] - } +The file should contain a set of hostnames followed each with their own +username/password pairs, for example: -This then will provide http basic authentication for two domains -serving packages with two different sets of credentials. +```json +{ + "basic-auth": [ + "repo.example1.org": { + "username": "my-username1", + "password": "my-secret-password1" + }, + "repo.example2.org": { + "username": "my-username2", + "password": "my-secret-password2" + } + ] +} +``` + +The main advantage of the auth.json file is that it can be gitignored so +that every developer in your team can place their own credentials in there, +which makes revokation of credentials much easier than if you all share the +same. From 3ff8bcffea11a0e1528d76b65c4765cfffadb460 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 8 Dec 2014 22:04:10 +0000 Subject: [PATCH 527/638] Allow loading of real composer repos by local path, fixes #1899 --- src/Composer/Command/CreateProjectCommand.php | 10 ++++++++-- src/Composer/Repository/ComposerRepository.php | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 935370bfc..4a3d33831 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -234,8 +234,14 @@ EOT { if (null === $repositoryUrl) { $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); - } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { - $sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config))); + } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION) && file_exists($repositoryUrl)) { + $json = new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config)); + $data = $json->read(); + if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) { + $sourceRepo = new ComposerRepository(array('url' => 'file://' . strtr(realpath($repositoryUrl), '\\', '/')), $io, $config); + } else { + $sourceRepo = new FilesystemRepository($json); + } } elseif (0 === strpos($repositoryUrl, 'http')) { $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); } else { diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 459fa55a7..d5d8c11ef 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository $this->config = $config; $this->options = $repoConfig['options']; $this->url = $repoConfig['url']; - $this->baseUrl = rtrim(preg_replace('{^(.*)(?:/packages.json)?(?:[?#].*)?$}', '$1', $this->url), '/'); + $this->baseUrl = rtrim(preg_replace('{^(.*)(?:/[^/\\]+.json)?(?:[?#].*)?$}', '$1', $this->url), '/'); $this->io = $io; $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$'); $this->loader = new ArrayLoader(); @@ -395,7 +395,7 @@ class ComposerRepository extends ArrayRepository $jsonUrlParts = parse_url($this->url); - if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '/packages.json')) { + if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) { $jsonUrl = $this->url; } else { $jsonUrl = $this->url . '/packages.json'; From 10401d544bc536fa15ed88dda7a0c9d161dc24b6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 9 Dec 2014 11:32:02 +0000 Subject: [PATCH 528/638] Avoid leftovers in temp dir --- src/Composer/Package/Archiver/ArchiveManager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 55d87c1a3..d600a7a75 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -137,7 +137,7 @@ class ArchiveManager $sourcePath = realpath('.'); } else { // Directory used to download the sources - $sourcePath = sys_get_temp_dir().'/composer_archiver/arch'.uniqid(); + $sourcePath = sys_get_temp_dir().'/composer_archive'.uniqid(); $filesystem->ensureDirectoryExists($sourcePath); // Download sources @@ -154,7 +154,7 @@ class ArchiveManager } // Create the archive - $tempTarget = sys_get_temp_dir().'/composer_archiver/arch'.uniqid().'.'.$format; + $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes()); @@ -164,6 +164,7 @@ class ArchiveManager if (!$package instanceof RootPackageInterface) { $filesystem->removeDirectory($sourcePath); } + $filesystem->remove($tempTarget); return $target; } From 6ccc80409442c2c7f440eb2c84e367a8a53fb135 Mon Sep 17 00:00:00 2001 From: Thijs Lensselink Date: Tue, 9 Dec 2014 21:41:31 +0100 Subject: [PATCH 529/638] Fix #3521 version mismatch when running diag, Replaced git command to get current checked out tag --- src/Composer/Compiler.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index e410c1878..2f1e1888f 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -55,9 +55,12 @@ class Compiler $date->setTimezone(new \DateTimeZone('UTC')); $this->versionDate = $date->format('Y-m-d H:i:s'); - $process = new Process('git describe --tags HEAD'); + $process = new Process('git name-rev --tags --name-only $(git rev-parse HEAD)'); if ($process->run() == 0) { - $this->version = trim($process->getOutput()); + $output = trim($process->getOutput()); + if ($output != 'undefined') { + $this->version = $output; + } } else { // get branch-alias defined in composer.json for dev-master (if any) $localConfig = __DIR__.'/../../composer.json'; From 2131c8b48082a176565856d69c6273047c96a6dc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 9 Dec 2014 22:39:59 +0000 Subject: [PATCH 530/638] Simplify tag probe --- src/Composer/Compiler.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 2f1e1888f..8697d8caf 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -55,12 +55,9 @@ class Compiler $date->setTimezone(new \DateTimeZone('UTC')); $this->versionDate = $date->format('Y-m-d H:i:s'); - $process = new Process('git name-rev --tags --name-only $(git rev-parse HEAD)'); + $process = new Process('git describe --tags --exact-match HEAD'); if ($process->run() == 0) { - $output = trim($process->getOutput()); - if ($output != 'undefined') { - $this->version = $output; - } + $this->version = trim($process->getOutput()); } else { // get branch-alias defined in composer.json for dev-master (if any) $localConfig = __DIR__.'/../../composer.json'; From 4a3bc58adfd501fa6e82c82fdd9e8d4036898fa1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Dec 2014 15:36:16 +0000 Subject: [PATCH 531/638] Fix formatting --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index bebac5355..7f00e762a 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -75,7 +75,7 @@ Version constraints can be specified in a few different ways. Name | Example | Description -------------- | ------------------------------------------------------------------------ | ----------- Exact version | `1.0.2` | You can specify the exact version of a package. -Range | `>=1.0` `>=1.0 <2.0` >=1.0 <1.1 || >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (` `) or comma (`,`) will be treated as a **logical AND**. A double pipe (||) will be treated as a **logical OR**. AND has higher precedence than OR. +Range | `>=1.0` `>=1.0 <2.0` >=1.0 <1.1 || >=1.2 | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space ( ) or comma (`,`) will be treated as a **logical AND**. A double pipe (||) will be treated as a **logical OR**. AND has higher precedence than OR. Hyphen Range | `1.0 - 2.0` | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`. Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2 <2.0`. For more details, read the next section below. From 4a6503fe36bcc2323d8e7626f72b576d1c5130b1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Dec 2014 17:42:55 +0000 Subject: [PATCH 532/638] Add suppor for https_proxy, fixes #3204 --- src/Composer/Util/StreamContextFactory.php | 77 ++++++++++--------- .../Test/Util/StreamContextFactoryTest.php | 23 +++++- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 1c3c20e44..c3b9e2773 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -43,6 +43,19 @@ final class StreamContextFactory $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']); } + // Override with HTTPS proxy if present and URL is https + if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) { + $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']); + } + + // Remove proxy if URL matches no_proxy directive + if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) { + $pattern = new NoProxyPattern($_SERVER['no_proxy']); + if ($pattern->test($url)) { + unset($proxy); + } + } + if (!empty($proxy)) { $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : ''; $proxyURL .= isset($proxy['host']) ? $proxy['host'] : ''; @@ -64,48 +77,38 @@ final class StreamContextFactory $options['http']['proxy'] = $proxyURL; - // Handle no_proxy directive - if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) { - $pattern = new NoProxyPattern($_SERVER['no_proxy']); - if ($pattern->test($url)) { - unset($options['http']['proxy']); - } + // enabled request_fulluri unless it is explicitly disabled + switch (parse_url($url, PHP_URL_SCHEME)) { + case 'http': // default request_fulluri to true + $reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI'); + if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { + $options['http']['request_fulluri'] = true; + } + break; + case 'https': // default request_fulluri to true + $reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI'); + if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { + $options['http']['request_fulluri'] = true; + } + break; } - // add request_fulluri and authentication if we still have a proxy to connect to - if (!empty($options['http']['proxy'])) { - // enabled request_fulluri unless it is explicitly disabled - switch (parse_url($url, PHP_URL_SCHEME)) { - case 'http': // default request_fulluri to true - $reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI'); - if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { - $options['http']['request_fulluri'] = true; - } - break; - case 'https': // default request_fulluri to true - $reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI'); - if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { - $options['http']['request_fulluri'] = true; - } - break; + // handle proxy auth if present + if (isset($proxy['user'])) { + $auth = urldecode($proxy['user']); + if (isset($proxy['pass'])) { + $auth .= ':' . urldecode($proxy['pass']); } + $auth = base64_encode($auth); - if (isset($proxy['user'])) { - $auth = urldecode($proxy['user']); - if (isset($proxy['pass'])) { - $auth .= ':' . urldecode($proxy['pass']); - } - $auth = base64_encode($auth); - - // Preserve headers if already set in default options - if (isset($defaultOptions['http']['header'])) { - if (is_string($defaultOptions['http']['header'])) { - $defaultOptions['http']['header'] = array($defaultOptions['http']['header']); - } - $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}"; - } else { - $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}"); + // Preserve headers if already set in default options + if (isset($defaultOptions['http']['header'])) { + if (is_string($defaultOptions['http']['header'])) { + $defaultOptions['http']['header'] = array($defaultOptions['http']['header']); } + $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}"; + } else { + $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}"); } } } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 5415a8c11..83148bced 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -20,6 +20,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { unset($_SERVER['HTTP_PROXY']); unset($_SERVER['http_proxy']); + unset($_SERVER['HTTPS_PROXY']); + unset($_SERVER['https_proxy']); unset($_SERVER['no_proxy']); } @@ -27,6 +29,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { unset($_SERVER['HTTP_PROXY']); unset($_SERVER['http_proxy']); + unset($_SERVER['HTTPS_PROXY']); + unset($_SERVER['https_proxy']); unset($_SERVER['no_proxy']); } @@ -126,7 +130,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; - $context = StreamContextFactory::getContext('http://example.org', array('http' => array('method' => 'GET'))); + $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); $this->assertEquals(array('http' => array( @@ -139,6 +143,23 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase )), $options); } + public function testHttpsProxyOverride() + { + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; + $_SERVER['http_proxy'] = 'https://woopproxy.net'; + + $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); + $options = stream_context_get_options($context); + + $this->assertEquals(array('http' => array( + 'proxy' => 'ssl://woopproxy.net:443', + 'request_fulluri' => true, + 'method' => 'GET', + 'max_redirects' => 20, + 'follow_location' => 1, + )), $options); + } + /** * @dataProvider dataSSLProxy */ From 7b13507dd4d3b93578af7d83fbf8be0ca686f4b5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Dec 2014 21:52:29 +0000 Subject: [PATCH 533/638] Fix typo in test --- tests/Composer/Test/Util/StreamContextFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 83148bced..5003eb1ae 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -146,7 +146,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase public function testHttpsProxyOverride() { $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; - $_SERVER['http_proxy'] = 'https://woopproxy.net'; + $_SERVER['https_proxy'] = 'https://woopproxy.net'; $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); From 8dad8466136e58a595bd74f866f201a219d44936 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 12 Dec 2014 12:25:21 +0000 Subject: [PATCH 534/638] Add SNI settings for SSL URLs that are proxied, fixes #3204 --- src/Composer/Util/StreamContextFactory.php | 8 ++++ .../Test/Util/StreamContextFactoryTest.php | 48 +++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index c3b9e2773..5bcb431ca 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -93,6 +93,14 @@ final class StreamContextFactory break; } + // add SNI opts for https URLs + if ('https' === parse_url($url, PHP_URL_SCHEME)) { + $options['ssl']['SNI_enabled'] = true; + if (version_compare(PHP_VERSION, '5.6.0', '<')) { + $options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST); + } + } + // handle proxy auth if present if (isset($proxy['user'])) { $auth = urldecode($proxy['user']); diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 5003eb1ae..fe6fa77fd 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -133,14 +133,23 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); - $this->assertEquals(array('http' => array( - 'proxy' => 'tcp://proxyserver.net:80', - 'request_fulluri' => true, - 'method' => 'GET', - 'header' => array("Proxy-Authorization: Basic " . base64_encode('username:password')), - 'max_redirects' => 20, - 'follow_location' => 1, - )), $options); + $expected = array( + 'http' => array( + 'proxy' => 'tcp://proxyserver.net:80', + 'request_fulluri' => true, + 'method' => 'GET', + 'header' => array("Proxy-Authorization: Basic " . base64_encode('username:password')), + 'max_redirects' => 20, + 'follow_location' => 1, + ), 'ssl' => array( + 'SNI_enabled' => true, + 'SNI_server_name' => 'example.org' + ) + ); + if (version_compare(PHP_VERSION, '5.6.0', '>=')) { + unset($expected['ssl']['SNI_server_name']); + } + $this->assertEquals($expected, $options); } public function testHttpsProxyOverride() @@ -151,13 +160,22 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); - $this->assertEquals(array('http' => array( - 'proxy' => 'ssl://woopproxy.net:443', - 'request_fulluri' => true, - 'method' => 'GET', - 'max_redirects' => 20, - 'follow_location' => 1, - )), $options); + $expected = array( + 'http' => array( + 'proxy' => 'ssl://woopproxy.net:443', + 'request_fulluri' => true, + 'method' => 'GET', + 'max_redirects' => 20, + 'follow_location' => 1, + ), 'ssl' => array( + 'SNI_enabled' => true, + 'SNI_server_name' => 'example.org' + ) + ); + if (version_compare(PHP_VERSION, '5.6.0', '>=')) { + unset($expected['ssl']['SNI_server_name']); + } + $this->assertEquals($expected, $options); } /** From da881c118ba9e1ac88fddb68451dd8866ca27f59 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 12 Dec 2014 13:15:34 +0000 Subject: [PATCH 535/638] Fix failures --- tests/Composer/Test/CacheTest.php | 6 ++++++ tests/Composer/Test/Util/StreamContextFactoryTest.php | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php index 80c7de6bf..0651ed512 100644 --- a/tests/Composer/Test/CacheTest.php +++ b/tests/Composer/Test/CacheTest.php @@ -46,6 +46,9 @@ class CacheTest extends TestCase public function testRemoveOutdatedFiles() { + // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures + usleep(50000); + $outdated = array_slice($this->files, 1); $this->finder ->expects($this->once()) @@ -66,6 +69,9 @@ class CacheTest extends TestCase public function testRemoveFilesWhenCacheIsTooLarge() { + // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures + usleep(50000); + $emptyFinder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock(); $emptyFinder ->expects($this->once()) diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index fe6fa77fd..afb6bfc7e 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -154,6 +154,10 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase public function testHttpsProxyOverride() { + if (!extension_loaded('openssl')) { + $this->markTestSkipped('Requires openssl'); + } + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; $_SERVER['https_proxy'] = 'https://woopproxy.net'; From 55895ab494db249bc0fcd836237c3a23c9f36dfc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 12 Dec 2014 13:33:45 +0000 Subject: [PATCH 536/638] Skip tests on hhvm --- tests/Composer/Test/CacheTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php index 0651ed512..fbd989805 100644 --- a/tests/Composer/Test/CacheTest.php +++ b/tests/Composer/Test/CacheTest.php @@ -46,6 +46,10 @@ class CacheTest extends TestCase public function testRemoveOutdatedFiles() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('Test causes intermittent failures with HHVM on Travis'); + } + // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures usleep(50000); @@ -69,6 +73,10 @@ class CacheTest extends TestCase public function testRemoveFilesWhenCacheIsTooLarge() { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('Test causes intermittent failures with HHVM on Travis'); + } + // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures usleep(50000); From 113606be7bb04720531c8c0d99abdb15c3bcf697 Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Fri, 12 Dec 2014 17:12:32 +0100 Subject: [PATCH 537/638] should fix composer/composer#3382 --- .../EventDispatcher/EventDispatcher.php | 29 +++++++++++++++++++ .../EventDispatcher/EventDispatcherTest.php | 16 ++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 8d5ee9fa1..22a5f7133 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -155,6 +155,7 @@ class EventDispatcher $return = 0; foreach ($listeners as $callable) { if (!is_string($callable) && is_callable($callable)) { + $event = $this->checkListenerExpectedEvent($callable, $event); $return = false === call_user_func($callable, $event) ? 1 : 0; } elseif ($this->isPhpScript($callable)) { $className = substr($callable, 0, strpos($callable, '::')); @@ -200,9 +201,37 @@ class EventDispatcher */ protected function executeEventPhpScript($className, $methodName, Event $event) { + $event = $this->checkListenerExpectedEvent(array($className, $methodName), $event); + return $className::$methodName($event); } + /** + * @param mixed $target + * @param Event $event + * @return Event|CommandEvent + */ + protected function checkListenerExpectedEvent($target, Event $event) + { + if (!$event instanceof Script\Event) { + return $event; + } + + try { + $reflected = new \ReflectionParameter($target, 0); + } catch (\ReflectionException $e) { + return $event; + } + + $expected = $reflected->getClass()->name; + + if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') { + $event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments()); + } + + return $event; + } + /** * Add a listener for a particular event * diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index 69e1de290..c90c948ee 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -17,6 +17,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Installer\InstallerEvents; use Composer\TestCase; use Composer\Script\ScriptEvents; +use Composer\Script\CommandEvent; use Composer\Util\ProcessExecutor; class EventDispatcherTest extends TestCase @@ -38,6 +39,16 @@ class EventDispatcherTest extends TestCase $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); } + public function testDispatcherCanConvertScriptEventToCommandEventForListener() + { + $io = $this->getMock('Composer\IO\IOInterface'); + $dispatcher = $this->getDispatcherStubForListenersTest(array( + "Composer\Test\EventDispatcher\EventDispatcherTest::convertEvent" + ), $io); + + $this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false)); + } + /** * @dataProvider getValidCommands * @param string $command @@ -205,6 +216,11 @@ class EventDispatcherTest extends TestCase throw new \RuntimeException(); } + public static function convertEvent(CommandEvent $event) + { + return false; + } + public static function someMethod() { return true; From d9b8b3611b7ed4e52f77f3f74f6257c0b79cf0a7 Mon Sep 17 00:00:00 2001 From: Rob Date: Fri, 12 Dec 2014 22:33:04 +0100 Subject: [PATCH 538/638] fixes composer/composer#3546 --- .../EventDispatcher/EventDispatcher.php | 8 ++++++- .../EventDispatcher/EventDispatcherTest.php | 21 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 22a5f7133..83e96d622 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -223,7 +223,13 @@ class EventDispatcher return $event; } - $expected = $reflected->getClass()->name; + $typehint = $reflected->getClass(); + + if (!$typehint instanceof \ReflectionClass) { + return $event; + } + + $expected = $typehint->getName(); if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') { $event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments()); diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index c90c948ee..aaf8b6267 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -29,7 +29,7 @@ class EventDispatcherTest extends TestCase { $io = $this->getMock('Composer\IO\IOInterface'); $dispatcher = $this->getDispatcherStubForListenersTest(array( - "Composer\Test\EventDispatcher\EventDispatcherTest::call" + 'Composer\Test\EventDispatcher\EventDispatcherTest::call' ), $io); $io->expects($this->once()) @@ -43,7 +43,17 @@ class EventDispatcherTest extends TestCase { $io = $this->getMock('Composer\IO\IOInterface'); $dispatcher = $this->getDispatcherStubForListenersTest(array( - "Composer\Test\EventDispatcher\EventDispatcherTest::convertEvent" + 'Composer\Test\EventDispatcher\EventDispatcherTest::expectsCommandEvent' + ), $io); + + $this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false)); + } + + public function testDispatcherDoesNotAttemptConversionForListenerWithoutTypehint() + { + $io = $this->getMock('Composer\IO\IOInterface'); + $dispatcher = $this->getDispatcherStubForListenersTest(array( + 'Composer\Test\EventDispatcher\EventDispatcherTest::expectsVariableEvent' ), $io); $this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false)); @@ -216,7 +226,12 @@ class EventDispatcherTest extends TestCase throw new \RuntimeException(); } - public static function convertEvent(CommandEvent $event) + public static function expectsCommandEvent(CommandEvent $event) + { + return false; + } + + public static function expectsVariableEvent($event) { return false; } From 6ddd8d4ec755b2fe9056d20b5437343cd2feb7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Mo=CC=88ller?= Date: Sat, 13 Dec 2014 00:17:14 +0100 Subject: [PATCH 539/638] Enhancement: Add sort-packages option which allows sorting of packages --- src/Composer/Command/RequireCommand.php | 9 ++-- src/Composer/Json/JsonManipulator.php | 9 +++- .../Test/Json/JsonManipulatorTest.php | 52 +++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 55e3752a9..91043ad71 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -47,6 +47,7 @@ class RequireCommand extends InitCommand new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'), new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), )) ->setHelp(<<parseConstraints($constraint); } - if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey)) { + $sortPackages = $input->getOption('sort-packages'); + + if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; @@ -160,14 +163,14 @@ EOT return $status; } - private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey) + private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey, $sortPackages) { $contents = file_get_contents($json->getPath()); $manipulator = new JsonManipulator($contents); foreach ($new as $package => $constraint) { - if (!$manipulator->addLink($requireKey, $package, $constraint)) { + if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) { return false; } if (!$manipulator->removeSubNode($removeKey, $package)) { diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index dd8c49ebd..751e9021e 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -52,7 +52,7 @@ class JsonManipulator return $this->contents . $this->newline; } - public function addLink($type, $package, $constraint) + public function addLink($type, $package, $constraint, $sortPackages = false) { $decoded = JsonFile::parseJson($this->contents); @@ -90,6 +90,13 @@ class JsonManipulator } } + if (true === $sortPackages) { + $requirements = json_decode($links, true); + + ksort($requirements); + $links = $this->format($requirements); + } + $this->contents = $matches[1] . $matches[2] . $links . $matches[4]; return true; diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 1a0f976b5..7ffaaabc7 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -281,6 +281,58 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase ); } + /** + * @dataProvider providerAddLinkAndSortPackages + */ + public function testAddLinkAndSortPackages($json, $type, $package, $constraint, $sortPackages, $expected) + { + $manipulator = new JsonManipulator($json); + $this->assertTrue($manipulator->addLink($type, $package, $constraint, $sortPackages)); + $this->assertEquals($expected, $manipulator->getContents()); + } + + public function providerAddLinkAndSortPackages() + { + return array( + array( + '{ + "require": { + "vendor/baz": "qux" + } +}', + 'require', + 'foo', + 'bar', + true, + '{ + "require": { + "foo": "bar", + "vendor/baz": "qux" + } +} +' + ), + array( + '{ + "require": { + "vendor/baz": "qux" + } +}', + 'require', + 'foo', + 'bar', + false, + '{ + "require": { + "vendor/baz": "qux", + "foo": "bar" + } +} +' + ), + ); + } + /** * @dataProvider removeSubNodeProvider */ From 4a0feb0189a08c1d2833672989e3296902c34a12 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Nov 2014 14:01:01 +0100 Subject: [PATCH 540/638] add --prefer-lowest-stable to update command --- src/Composer/Command/UpdateCommand.php | 2 + .../DependencyResolver/DefaultPolicy.php | 7 +++- src/Composer/Installer.php | 23 ++++++++++- .../DependencyResolver/DefaultPolicyTest.php | 16 ++++++++ .../update-prefer-lowest-stable.test | 40 +++++++++++++++++++ tests/Composer/Test/InstallerTest.php | 1 + 6 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index ebfd4cb2a..cf3cf8ca2 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -47,6 +47,7 @@ class UpdateCommand extends Command new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('prefer-lowest-stable', null, InputOption::VALUE_NONE, 'Forces all packages to their lowest stable version.'), )) ->setHelp(<<update command reads the composer.json file from the @@ -121,6 +122,7 @@ EOT ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setPreferLowestStable($input->getOption('prefer-lowest-stable')) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 10cbe101e..5ae6b2c31 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -24,10 +24,12 @@ use Composer\Package\LinkConstraint\VersionConstraint; class DefaultPolicy implements PolicyInterface { private $preferStable; + private $preferLowest; - public function __construct($preferStable = false) + public function __construct($preferStable = false, $preferLowest = false) { $this->preferStable = $preferStable; + $this->preferLowest = $preferLowest; } public function versionCompare(PackageInterface $a, PackageInterface $b, $operator) @@ -195,6 +197,7 @@ class DefaultPolicy implements PolicyInterface protected function pruneToBestVersion(Pool $pool, $literals) { + $operator = $this->preferLowest ? '<' : '>'; $bestLiterals = array($literals[0]); $bestPackage = $pool->literalToPackage($literals[0]); foreach ($literals as $i => $literal) { @@ -204,7 +207,7 @@ class DefaultPolicy implements PolicyInterface $package = $pool->literalToPackage($literal); - if ($this->versionCompare($package, $bestPackage, '>')) { + if ($this->versionCompare($package, $bestPackage, $operator)) { $bestPackage = $package; $bestLiterals = array($literal); } elseif ($this->versionCompare($package, $bestPackage, '==')) { diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 9e2c07021..cfc995fe2 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -107,6 +107,7 @@ class Installer protected $update = false; protected $runScripts = true; protected $ignorePlatformReqs = false; + protected $preferLowestStable = false; /** * Array of package names/globs flagged for update * @@ -693,6 +694,7 @@ class Installer private function createPolicy() { + $preferLowest = false; $preferStable = null; if (!$this->update && $this->locker->isLocked()) { $preferStable = $this->locker->getPreferStable(); @@ -700,10 +702,14 @@ class Installer // old lock file without prefer stable will return null // so in this case we use the composer.json info if (null === $preferStable) { - $preferStable = $this->package->getPreferStable(); + if ($this->preferLowestStable) { + $preferStable = $preferLowest = true; + } else { + $preferStable = $this->package->getPreferStable(); + } } - return new DefaultPolicy($preferStable); + return new DefaultPolicy($preferStable, $preferLowest); } private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo) @@ -1244,6 +1250,19 @@ class Installer return $this; } + /** + * Should packages be forced to their lowest stable version when updating? + * + * @param boolean $preferLowestStable + * @return Installer + */ + public function setPreferLowestStable($preferLowestStable = true) + { + $this->preferLowestStable = (boolean) $preferLowestStable; + + return $this; + } + /** * Disables plugins. * diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index 9e9952228..f06ba4437 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -247,4 +247,20 @@ class DefaultPolicyTest extends TestCase return $map; } + + public function testSelectLowest() + { + $policy = new DefaultPolicy(false, true); + + $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); + $this->pool->addRepository($this->repo); + + $literals = array($packageA1->getId(), $packageA2->getId()); + $expected = array($packageA1->getId()); + + $selected = $policy->selectPreferedPackages($this->pool, array(), $literals); + + $this->assertEquals($expected, $selected); + } } diff --git a/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test new file mode 100644 index 000000000..b511fdf72 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test @@ -0,0 +1,40 @@ +--TEST-- +Updates packages to their lowest stable version +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0-rc1" }, + { "name": "a/a", "version": "1.0.1" }, + { "name": "a/a", "version": "1.1.0" }, + + { "name": "a/b", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.1" }, + { "name": "a/b", "version": "2.0.0" }, + + { "name": "a/c", "version": "1.0.0" }, + { "name": "a/c", "version": "2.0.0" } + ] + } + ], + "require": { + "a/a": "~1.0@dev", + "a/c": "2.*" + }, + "require-dev": { + "a/b": "*" + } +} +--INSTALLED-- +[ + { "name": "a/a", "version": "1.0.0-rc1" }, + { "name": "a/c", "version": "2.0.0" }, + { "name": "a/b", "version": "1.0.1" } +] +--RUN-- +update --prefer-lowest-stable +--EXPECT-- +Updating a/a (1.0.0-rc1) to a/a (1.0.1) +Updating a/b (1.0.1) to a/b (1.0.0) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 4dc0bf511..27e1d84b9 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -217,6 +217,7 @@ class InstallerTest extends TestCase ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) + ->setPreferLowestStable($input->getOption('prefer-lowest-stable')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); return $installer->run(); From 98b254a3ec22d3d4916abf8f2b84fd2c289baad2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 2 Dec 2014 09:18:44 +0100 Subject: [PATCH 541/638] split update --prefer-lowest and --prefer-stable --- src/Composer/Command/UpdateCommand.php | 6 ++-- src/Composer/Installer.php | 31 +++++++++++++------ .../update-prefer-lowest-stable.test | 2 +- tests/Composer/Test/InstallerTest.php | 3 +- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index cf3cf8ca2..5e83658c9 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -47,7 +47,8 @@ class UpdateCommand extends Command new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), - new InputOption('prefer-lowest-stable', null, InputOption::VALUE_NONE, 'Forces all packages to their lowest stable version.'), + new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), + new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), )) ->setHelp(<<update command reads the composer.json file from the @@ -122,7 +123,8 @@ EOT ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) - ->setPreferLowestStable($input->getOption('prefer-lowest-stable')) + ->setPreferStable($input->getOption('prefer-stable')) + ->setPreferLowest($input->getOption('prefer-lowest')) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index cfc995fe2..58459f7a6 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -107,7 +107,8 @@ class Installer protected $update = false; protected $runScripts = true; protected $ignorePlatformReqs = false; - protected $preferLowestStable = false; + protected $preferStable = false; + protected $preferLowest = false; /** * Array of package names/globs flagged for update * @@ -702,11 +703,8 @@ class Installer // old lock file without prefer stable will return null // so in this case we use the composer.json info if (null === $preferStable) { - if ($this->preferLowestStable) { - $preferStable = $preferLowest = true; - } else { - $preferStable = $this->package->getPreferStable(); - } + $preferStable = $this->preferStable || $this->package->getPreferStable(); + $preferLowest = $this->preferLowest; } return new DefaultPolicy($preferStable, $preferLowest); @@ -1251,14 +1249,27 @@ class Installer } /** - * Should packages be forced to their lowest stable version when updating? + * Should packages be prefered in a stable version when updating? * - * @param boolean $preferLowestStable + * @param boolean $preferStable * @return Installer */ - public function setPreferLowestStable($preferLowestStable = true) + public function setPreferStable($preferStable = true) { - $this->preferLowestStable = (boolean) $preferLowestStable; + $this->preferStable = (boolean) $preferStable; + + return $this; + } + + /** + * Should packages be prefered in a lowest version when updating? + * + * @param boolean $preferLowest + * @return Installer + */ + public function setPreferLowest($preferLowest = true) + { + $this->preferLowest = (boolean) $preferLowest; return $this; } diff --git a/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test index b511fdf72..00efd5688 100644 --- a/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test +++ b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test @@ -34,7 +34,7 @@ Updates packages to their lowest stable version { "name": "a/b", "version": "1.0.1" } ] --RUN-- -update --prefer-lowest-stable +update --prefer-lowest --prefer-stable --EXPECT-- Updating a/a (1.0.0-rc1) to a/a (1.0.1) Updating a/b (1.0.1) to a/b (1.0.0) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 27e1d84b9..9bf2c01a7 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -217,7 +217,8 @@ class InstallerTest extends TestCase ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) ->setWhitelistDependencies($input->getOption('with-dependencies')) - ->setPreferLowestStable($input->getOption('prefer-lowest-stable')) + ->setPreferStable($input->getOption('prefer-stable')) + ->setPreferLowest($input->getOption('prefer-lowest')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); return $installer->run(); From 07ad1da3ac7faa035003cce8c2456829c1c47838 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 11:13:25 +0000 Subject: [PATCH 542/638] Fix HHVM interop --- src/Composer/EventDispatcher/EventDispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 83e96d622..c5033124e 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -219,7 +219,7 @@ class EventDispatcher try { $reflected = new \ReflectionParameter($target, 0); - } catch (\ReflectionException $e) { + } catch (\Exception $e) { return $event; } From aec1af4e11ec62f6842dc21bf0f35e25b6d3e9e4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 11:47:22 +0000 Subject: [PATCH 543/638] Point users at troubleshooting if they get known exceptions --- src/Composer/Console/Application.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 940864683..849884d1c 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -191,6 +191,16 @@ class Application extends BaseApplication } catch (\Exception $e) { } + if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) { + $output->writeln('The following exception may be caused by a stale entry in your cmd.exe AutoRun'); + $output->writeln('Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details'); + } + + if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) { + $output->writeln('The following exception is caused by a lack of memory and not having swap configured'); + $output->writeln('Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details'); + } + return parent::renderException($exception, $output); } From d515577560be74add00e068957bca76694b5af98 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 13:37:05 +0000 Subject: [PATCH 544/638] Fix wording, refs #3466 --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 4e744c1d1..f9c07abf2 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -128,7 +128,7 @@ it will install the latest Composer version and set up your PATH so that you can just call `composer` from any directory in your command line. > **Note:** Close your current terminal. Test usage with a new terminal: -> Thats important because the PATH gets only loaded when the terminal gets started. +> That is important since the PATH only gets loaded when the terminal starts. ### Manual Installation From bef89604e2f8d9660d221a44852a084840404189 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 14:31:19 +0000 Subject: [PATCH 545/638] Fix parsing of strings, fixes #3543 --- src/Composer/Json/JsonManipulator.php | 2 +- tests/Composer/Test/Json/JsonManipulatorTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index 751e9021e..e719e3d6d 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -31,7 +31,7 @@ class JsonManipulator if (!self::$RECURSE_BLOCKS) { self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*'; self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*'; - self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])+"'; + self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"'; self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})'; } diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 7ffaaabc7..e3285efed 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -73,6 +73,7 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase ), array( '{ + "empty": "", "require": { "foo": "bar" } @@ -81,6 +82,7 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase 'vendor/baz', 'qux', '{ + "empty": "", "require": { "foo": "bar", "vendor/baz": "qux" From cc4223e6f9f66cd7c8ba78dc7a8442d1029ab2d2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 14:54:34 +0000 Subject: [PATCH 546/638] Avoid loading plugins that have had their code wiped from filesystem, fixes #3115, closes #3538 --- src/Composer/Factory.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 4f7bfefd8..327709d3f 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -284,13 +284,13 @@ class Factory $pm = $this->createPluginManager($composer, $io, $globalRepository); $composer->setPluginManager($pm); + // purge packages if they have been deleted on the filesystem + $this->purgePackages($rm, $im); + if (!$disablePlugins) { $pm->loadInstalledPlugins(); } - // purge packages if they have been deleted on the filesystem - $this->purgePackages($rm, $im); - // init locker if possible if (isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) From 8c3ee037342a92ea157a569e86d61d3d6f680c13 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 15:14:54 +0000 Subject: [PATCH 547/638] Enough intermittent failures already --- tests/Composer/Test/CacheTest.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php index fbd989805..0585e78b8 100644 --- a/tests/Composer/Test/CacheTest.php +++ b/tests/Composer/Test/CacheTest.php @@ -46,13 +46,10 @@ class CacheTest extends TestCase public function testRemoveOutdatedFiles() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Test causes intermittent failures with HHVM on Travis'); + if (getenv('TRAVIS')) { + $this->markTestSkipped('Test causes intermittent failures on Travis'); } - // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures - usleep(50000); - $outdated = array_slice($this->files, 1); $this->finder ->expects($this->once()) @@ -73,13 +70,10 @@ class CacheTest extends TestCase public function testRemoveFilesWhenCacheIsTooLarge() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Test causes intermittent failures with HHVM on Travis'); + if (getenv('TRAVIS')) { + $this->markTestSkipped('Test causes intermittent failures on Travis'); } - // sleeping a bit to let the filesystem cool down on travis or it has intermittent failures - usleep(50000); - $emptyFinder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock(); $emptyFinder ->expects($this->once()) From dcd2c9c7c6d202614185a6466c05af83ba6b2019 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 15:49:35 +0000 Subject: [PATCH 548/638] Make sure global plugins are purged as well, fixes #3115 --- src/Composer/Factory.php | 83 ++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 327709d3f..1336a6663 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -18,6 +18,7 @@ use Composer\IO\IOInterface; use Composer\Package\Archiver; use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryInterface; +use Composer\Repository\WritableRepositoryInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; use Composer\Util\Filesystem; @@ -186,11 +187,12 @@ class Factory * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will * read from the default filename * @param bool $disablePlugins Whether plugins should not be loaded + * @param bool $minimalLoad Whether to initialize everything or only main project stuff (used when loading the global composer) * @throws \InvalidArgumentException * @throws \UnexpectedValueException * @return Composer */ - public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false) + public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $minimalLoad = false) { // load Composer configuration if (null === $localConfig) { @@ -232,24 +234,28 @@ class Factory } } - // load auth configs into the IO instance - $io->loadConfiguration($config); - $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); - // setup process timeout - ProcessExecutor::setTimeout((int) $config->get('process-timeout')); - // initialize composer $composer = new Composer(); $composer->setConfig($config); + if (!$minimalLoad) { + // load auth configs into the IO instance + $io->loadConfiguration($config); + + // setup process timeout + ProcessExecutor::setTimeout((int) $config->get('process-timeout')); + } + // initialize event dispatcher $dispatcher = new EventDispatcher($composer, $io); + $composer->setEventDispatcher($dispatcher); // initialize repository manager $rm = $this->createRepositoryManager($io, $config, $dispatcher); + $composer->setRepositoryManager($rm); // load local repository $this->addLocalRepository($rm, $vendorDir); @@ -258,41 +264,39 @@ class Factory $parser = new VersionParser; $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io)); $package = $loader->load($localConfig); + $composer->setPackage($package); // initialize installation manager $im = $this->createInstallationManager(); - - // Composer composition - $composer->setPackage($package); - $composer->setRepositoryManager($rm); $composer->setInstallationManager($im); - // initialize download manager - $dm = $this->createDownloadManager($io, $config, $dispatcher); - - $composer->setDownloadManager($dm); - $composer->setEventDispatcher($dispatcher); - - // initialize autoload generator - $generator = new AutoloadGenerator($dispatcher, $io); - $composer->setAutoloadGenerator($generator); - // add installers to the manager $this->createDefaultInstallers($im, $composer, $io); - $globalRepository = $this->createGlobalRepository($config, $vendorDir); + if (!$minimalLoad) { + // initialize download manager + $dm = $this->createDownloadManager($io, $config, $dispatcher); + $composer->setDownloadManager($dm); + + // initialize autoload generator + $generator = new AutoloadGenerator($dispatcher, $io); + $composer->setAutoloadGenerator($generator); + } + + $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); + $globalRepository = $globalComposer ? $globalComposer->getRepositoryManager()->getLocalRepository() : null; $pm = $this->createPluginManager($composer, $io, $globalRepository); $composer->setPluginManager($pm); // purge packages if they have been deleted on the filesystem - $this->purgePackages($rm, $im); + $this->purgePackages($rm->getLocalRepository(), $im); if (!$disablePlugins) { $pm->loadInstalledPlugins(); } // init locker if possible - if (isset($composerFile)) { + if (!$minimalLoad && isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock'; @@ -335,22 +339,26 @@ class Factory } /** - * @param Config $config - * @param string $vendorDir - * @return Repository\InstalledFilesystemRepository|null + * @param Config $config + * @return Composer|null */ - protected function createGlobalRepository(Config $config, $vendorDir) + protected function createGlobalComposer(IOInterface $io, Config $config, $disablePlugins) { - if ($config->get('home') == $vendorDir) { - return null; + $oldCwd = getcwd(); + if (realpath($config->get('home')) === realpath($oldCwd)) { + return; } - $path = $config->get('home').'/vendor/composer/installed.json'; - if (!file_exists($path)) { - return null; + $composer = null; + chdir($config->get('home')); + try { + $composer = self::createComposer($io, null, $disablePlugins, true); + } catch (\Exception $e) { + throw $e; } + chdir($oldCwd); - return new Repository\InstalledFilesystemRepository(new JsonFile($path)); + return $composer; } /** @@ -447,12 +455,11 @@ class Factory } /** - * @param Repository\RepositoryManager $rm - * @param Installer\InstallationManager $im + * @param WritableRepositoryInterface $repo repository to purge packages from + * @param Installer\InstallationManager $im manager to check whether packages are still installed */ - protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) + protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im) { - $repo = $rm->getLocalRepository(); foreach ($repo->getPackages() as $package) { if (!$im->isPackageInstalled($repo, $package)) { $repo->removePackage($package); From cf9d5f637b2d6c14fef3198b5cf212782b052e6f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 15:58:33 +0000 Subject: [PATCH 549/638] Suppress errors when creating global composer --- src/Composer/Factory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 1336a6663..e8fd8fb92 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -354,7 +354,6 @@ class Factory try { $composer = self::createComposer($io, null, $disablePlugins, true); } catch (\Exception $e) { - throw $e; } chdir($oldCwd); From 90a29c127fbd3112aab019f3a5e5f37afcd3ae9c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 16:10:51 +0000 Subject: [PATCH 550/638] Fix bug when installing --- src/Composer/Factory.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e8fd8fb92..b41bfd60d 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -187,12 +187,12 @@ class Factory * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will * read from the default filename * @param bool $disablePlugins Whether plugins should not be loaded - * @param bool $minimalLoad Whether to initialize everything or only main project stuff (used when loading the global composer) + * @param bool $fullLoad Whether to initialize everything or only main project stuff (used when loading the global composer) * @throws \InvalidArgumentException * @throws \UnexpectedValueException * @return Composer */ - public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $minimalLoad = false) + public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $fullLoad = true) { // load Composer configuration if (null === $localConfig) { @@ -241,7 +241,7 @@ class Factory $composer = new Composer(); $composer->setConfig($config); - if (!$minimalLoad) { + if ($fullLoad) { // load auth configs into the IO instance $io->loadConfiguration($config); @@ -270,10 +270,7 @@ class Factory $im = $this->createInstallationManager(); $composer->setInstallationManager($im); - // add installers to the manager - $this->createDefaultInstallers($im, $composer, $io); - - if (!$minimalLoad) { + if ($fullLoad) { // initialize download manager $dm = $this->createDownloadManager($io, $config, $dispatcher); $composer->setDownloadManager($dm); @@ -283,6 +280,9 @@ class Factory $composer->setAutoloadGenerator($generator); } + // add installers to the manager (must happen after download manager is created since they read it out of $composer) + $this->createDefaultInstallers($im, $composer, $io); + $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); $globalRepository = $globalComposer ? $globalComposer->getRepositoryManager()->getLocalRepository() : null; $pm = $this->createPluginManager($composer, $io, $globalRepository); @@ -296,7 +296,7 @@ class Factory } // init locker if possible - if (!$minimalLoad && isset($composerFile)) { + if ($fullLoad && isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock'; @@ -338,7 +338,7 @@ class Factory $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json'))); } - /** + /** * @param Config $config * @return Composer|null */ @@ -352,7 +352,7 @@ class Factory $composer = null; chdir($config->get('home')); try { - $composer = self::createComposer($io, null, $disablePlugins, true); + $composer = self::createComposer($io, null, $disablePlugins, false); } catch (\Exception $e) { } chdir($oldCwd); From 1622b9a390581753d28e83941275459c297fe86c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 16:14:03 +0000 Subject: [PATCH 551/638] Fix tests --- src/Composer/Factory.php | 4 +++- tests/Composer/Test/Mock/FactoryMock.php | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index b41bfd60d..5b2539672 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -289,7 +289,9 @@ class Factory $composer->setPluginManager($pm); // purge packages if they have been deleted on the filesystem - $this->purgePackages($rm->getLocalRepository(), $im); + if ($rm->getLocalRepository()) { + $this->purgePackages($rm->getLocalRepository(), $im); + } if (!$disablePlugins) { $pm->loadInstalledPlugins(); diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php index 75d2a23bb..ccbda8040 100644 --- a/tests/Composer/Test/Mock/FactoryMock.php +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -16,6 +16,7 @@ use Composer\Config; use Composer\Factory; use Composer\Repository; use Composer\Repository\RepositoryManager; +use Composer\Repository\WritableRepositoryInterface; use Composer\Installer; use Composer\IO\IOInterface; @@ -46,7 +47,7 @@ class FactoryMock extends Factory { } - protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) + protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im) { } } From d26f817cfde9da7057e64be5d748803f26948c26 Mon Sep 17 00:00:00 2001 From: Rob Date: Sat, 13 Dec 2014 18:59:14 +0100 Subject: [PATCH 552/638] rewriting some documentation sections --- doc/00-intro.md | 26 ++++++++++---------------- doc/01-basic-usage.md | 25 ++----------------------- doc/03-cli.md | 4 ++++ 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index f9c07abf2..a194d9de8 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -47,7 +47,7 @@ any version beginning with `1.2`. ## System Requirements Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile -flags are also required, but the installer will warn you about any +flags are also required, but when using the installer you will be warned about any incompatibilities. To install packages from sources instead of simple zip archives, you will need @@ -58,12 +58,17 @@ Linux and OSX. ## Installation - *nix +These instructions should cover most Linux and Unix variants (including OSX). + ### Downloading the Composer Executable +There are in short, two ways to install Composer on *nix. Locally as part of your +project, or globally as a system wide executable. + #### Locally -To actually get Composer, we need to do two things. The first one is installing -Composer (again, this means downloading it into your project): +Installing Composer locally is a matter of just running the installer in your +project directory: ```sh curl -sS https://getcomposer.org/installer | php @@ -76,8 +81,8 @@ curl -sS https://getcomposer.org/installer | php php -r "readfile('https://getcomposer.org/installer');" | php ``` -This will just check a few PHP settings and then download `composer.phar` to -your working directory. This file is the Composer binary. It is a PHAR (PHP +The installer will just check a few PHP settings and then download `composer.phar` +to your working directory. This file is the Composer binary. It is a PHAR (PHP archive), which is an archive format for PHP which can be run on the command line, amongst other things. @@ -106,17 +111,6 @@ mv composer.phar /usr/local/bin/composer Then, just run `composer` in order to run Composer instead of `php composer.phar`. -#### Globally (on OSX via homebrew) - -Composer is part of the homebrew-php project. - -```sh -brew update -brew tap homebrew/dupes -brew tap homebrew/php -brew install composer -``` - ## Installation - Windows ### Using the Installer diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 7f00e762a..1ab4acbdb 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -1,29 +1,8 @@ # Basic usage -## Installation +## Installing -To install Composer, you just need to download the `composer.phar` executable. - -```sh -curl -sS https://getcomposer.org/installer | php -``` - -For the details, see the [Introduction](00-intro.md) chapter. - -To check if Composer is working, just run the PHAR through `php`: - -```sh -php composer.phar -``` - -This should give you a list of available commands. - -> **Note:** You can also perform the checks only without downloading Composer -> by using the `--check` option. For more information, just use `--help`. -> -> ```sh -> curl -sS https://getcomposer.org/installer | php -- --help -> ``` +If you have not yet installed Composer, refer to to the [Intro](00-intro.md) chapter. ## `composer.json`: Project Setup diff --git a/doc/03-cli.md b/doc/03-cli.md index 69f8f58c5..60599e5cd 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -482,6 +482,10 @@ performance. a bit of time to run so it is currently not done by default. * **--no-dev:** Disables autoload-dev rules. +## clear-cache + +Deletes all content from Composer's cache directories. + ## licenses Lists the name, version and license of every package installed. Use From 2e4cb8be33bc62c1f7b258e77123997c7e1200d2 Mon Sep 17 00:00:00 2001 From: Rob Date: Sat, 13 Dec 2014 19:05:52 +0100 Subject: [PATCH 553/638] scrapping next line --- doc/00-intro.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index a194d9de8..c1bafa3cd 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -56,13 +56,11 @@ git, svn or hg depending on how the package is version-controlled. Composer is multi-platform and we strive to make it run equally well on Windows, Linux and OSX. -## Installation - *nix - -These instructions should cover most Linux and Unix variants (including OSX). +## Installation - Linux / Unix / OSX ### Downloading the Composer Executable -There are in short, two ways to install Composer on *nix. Locally as part of your +There are in short, two ways to install Composer. Locally as part of your project, or globally as a system wide executable. #### Locally From 0627d846a5a4b4378ad74851d877c3f5f34a6aaa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 18:53:29 +0000 Subject: [PATCH 554/638] PluginManager refactoring, fixes #3550 --- src/Composer/Console/Application.php | 2 +- src/Composer/Factory.php | 23 +++++++------ src/Composer/Plugin/PluginManager.php | 34 +++++++++---------- .../Test/Plugin/PluginInstallerTest.php | 2 +- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 849884d1c..8e49eedb5 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -177,7 +177,7 @@ class Application extends BaseApplication public function renderException($exception, $output) { try { - $composer = $this->getComposer(false); + $composer = $this->getComposer(false, true); if ($composer) { $config = $composer->getConfig(); diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 5b2539672..8f7ba28e0 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -283,18 +283,19 @@ class Factory // add installers to the manager (must happen after download manager is created since they read it out of $composer) $this->createDefaultInstallers($im, $composer, $io); - $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); - $globalRepository = $globalComposer ? $globalComposer->getRepositoryManager()->getLocalRepository() : null; - $pm = $this->createPluginManager($composer, $io, $globalRepository); - $composer->setPluginManager($pm); - // purge packages if they have been deleted on the filesystem if ($rm->getLocalRepository()) { $this->purgePackages($rm->getLocalRepository(), $im); } - if (!$disablePlugins) { - $pm->loadInstalledPlugins(); + if ($fullLoad) { + $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); + $pm = $this->createPluginManager($io, $composer, $globalComposer); + $composer->setPluginManager($pm); + + if (!$disablePlugins) { + $pm->loadInstalledPlugins(); + } } // init locker if possible @@ -424,14 +425,14 @@ class Factory } /** - * @param Composer $composer * @param IOInterface $io - * @param RepositoryInterface $globalRepository + * @param Composer $composer + * @param Composer $globalComposer * @return Plugin\PluginManager */ - protected function createPluginManager(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null) + protected function createPluginManager(IOInterface $io, Composer $composer, Composer $globalComposer = null) { - return new Plugin\PluginManager($composer, $io, $globalRepository); + return new Plugin\PluginManager($io, $composer, $globalComposer); } /** diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index fdd952ae3..d7c2d8657 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -28,12 +28,13 @@ use Composer\DependencyResolver\Pool; * Plugin manager * * @author Nils Adermann + * @author Jordi Boggiano */ class PluginManager { protected $composer; protected $io; - protected $globalRepository; + protected $globalComposer; protected $versionParser; protected $plugins = array(); @@ -44,15 +45,15 @@ class PluginManager /** * Initializes plugin manager * - * @param Composer $composer * @param IOInterface $io - * @param RepositoryInterface $globalRepository + * @param Composer $composer + * @param Composer $globalComposer */ - public function __construct(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null) + public function __construct(IOInterface $io, Composer $composer, Composer $globalComposer = null) { - $this->composer = $composer; $this->io = $io; - $this->globalRepository = $globalRepository; + $this->composer = $composer; + $this->globalComposer = $globalComposer; $this->versionParser = new VersionParser(); } @@ -62,12 +63,12 @@ class PluginManager public function loadInstalledPlugins() { $repo = $this->composer->getRepositoryManager()->getLocalRepository(); - + $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; if ($repo) { $this->loadRepository($repo); } - if ($this->globalRepository) { - $this->loadRepository($this->globalRepository); + if ($globalRepo) { + $this->loadRepository($globalRepo); } } @@ -206,11 +207,13 @@ class PluginManager } $classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']); - $pool = new Pool('dev'); $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); + $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; + + $pool = new Pool('dev'); $pool->addRepository($localRepo); - if ($this->globalRepository) { - $pool->addRepository($this->globalRepository); + if ($globalRepo) { + $pool->addRepository($globalRepo); } $autoloadPackages = array($package->getName() => $package); @@ -219,7 +222,7 @@ class PluginManager $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(); foreach ($autoloadPackages as $autoloadPackage) { - $downloadPath = $this->getInstallPath($autoloadPackage, ($this->globalRepository && $this->globalRepository->hasPackage($autoloadPackage))); + $downloadPath = $this->getInstallPath($autoloadPackage, ($globalRepo && $globalRepo->hasPackage($autoloadPackage))); $autoloads[] = array($autoloadPackage, $downloadPath); } @@ -261,9 +264,6 @@ class PluginManager return $this->composer->getInstallationManager()->getInstallPath($package); } - $targetDir = $package->getTargetDir(); - $vendorDir = $this->composer->getConfig()->get('home').'/vendor'; - - return ($vendorDir ? $vendorDir.'/' : '').$package->getPrettyName().($targetDir ? '/'.$targetDir : ''); + return $this->globalComposer->getInstallationManager()->getInstallPath($package); } } diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index 1799eb2b8..a2090082f 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -76,7 +76,7 @@ class PluginInstallerTest extends \PHPUnit_Framework_TestCase $this->composer->setInstallationManager($im); $this->composer->setAutoloadGenerator($this->autoloadGenerator); - $this->pm = new PluginManager($this->composer, $this->io); + $this->pm = new PluginManager($this->io, $this->composer); $this->composer->setPluginManager($this->pm); $config->merge(array( From 4cc52afcc4debecf4fa7ce998198355fea98e656 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 13 Dec 2014 18:54:01 +0000 Subject: [PATCH 555/638] Update deps --- composer.lock | 134 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 43 deletions(-) diff --git a/composer.lock b/composer.lock index b13b38f37..0c7ff85ba 100644 --- a/composer.lock +++ b/composer.lock @@ -120,17 +120,17 @@ }, { "name": "symfony/console", - "version": "v2.6.0", + "version": "v2.6.1", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10" + "reference": "ef825fd9f809d275926547c9e57cbf14968793e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/d3bac228fd7a2aac9193e241b239880b3ba39a10", - "reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10", + "url": "https://api.github.com/repos/symfony/Console/zipball/ef825fd9f809d275926547c9e57cbf14968793e8", + "reference": "ef825fd9f809d275926547c9e57cbf14968793e8", "shasum": "" }, "require": { @@ -173,21 +173,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-11-20 13:24:23" + "time": "2014-12-02 20:19:20" }, { "name": "symfony/finder", - "version": "v2.6.0", + "version": "v2.6.1", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", "url": "https://github.com/symfony/Finder.git", - "reference": "d574347c652a14cfee0349f744c7880e1d9029fd" + "reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/d574347c652a14cfee0349f744c7880e1d9029fd", - "reference": "d574347c652a14cfee0349f744c7880e1d9029fd", + "url": "https://api.github.com/repos/symfony/Finder/zipball/0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721", + "reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721", "shasum": "" }, "require": { @@ -220,21 +220,21 @@ ], "description": "Symfony Finder Component", "homepage": "http://symfony.com", - "time": "2014-11-28 10:00:40" + "time": "2014-12-02 20:19:20" }, { "name": "symfony/process", - "version": "v2.6.0", + "version": "v2.6.1", "target-dir": "Symfony/Component/Process", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", - "reference": "dc88f75d1c07791e5733f90be747961dce26cf05" + "reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/dc88f75d1c07791e5733f90be747961dce26cf05", - "reference": "dc88f75d1c07791e5733f90be747961dce26cf05", + "url": "https://api.github.com/repos/symfony/Process/zipball/bf0c9bd625f13b0b0bbe39919225cf145dfb935a", + "reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a", "shasum": "" }, "require": { @@ -267,7 +267,7 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "2014-11-04 14:29:39" + "time": "2014-12-02 20:19:20" } ], "packages-dev": [ @@ -327,16 +327,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.0.12", + "version": "2.0.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7ce9da20f96964bb7a4033f53834df13328dbeab" + "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ce9da20f96964bb7a4033f53834df13328dbeab", - "reference": "7ce9da20f96964bb7a4033f53834df13328dbeab", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5", + "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5", "shasum": "" }, "require": { @@ -388,7 +388,7 @@ "testing", "xunit" ], - "time": "2014-12-02 13:17:01" + "time": "2014-12-03 06:41:44" }, { "name": "phpunit/php-file-iterator", @@ -574,16 +574,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.3.5", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1" + "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0", + "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0", "shasum": "" }, "require": { @@ -600,8 +600,9 @@ "phpunit/phpunit-mock-objects": "~2.3", "sebastian/comparator": "~1.0", "sebastian/diff": "~1.1", - "sebastian/environment": "~1.0", + "sebastian/environment": "~1.1", "sebastian/exporter": "~1.0", + "sebastian/global-state": "~1.0", "sebastian/version": "~1.0", "symfony/yaml": "~2.0" }, @@ -614,7 +615,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3.x-dev" + "dev-master": "4.4.x-dev" } }, "autoload": { @@ -623,10 +624,6 @@ ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], "license": [ "BSD-3-Clause" ], @@ -638,13 +635,13 @@ } ], "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", + "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], - "time": "2014-11-11 10:11:09" + "time": "2014-12-05 06:49:03" }, { "name": "phpunit/phpunit-mock-objects", @@ -703,16 +700,16 @@ }, { "name": "sebastian/comparator", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef" + "reference": "c484a80f97573ab934e37826dba0135a3301b26a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", - "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a", + "reference": "c484a80f97573ab934e37826dba0135a3301b26a", "shasum": "" }, "require": { @@ -726,7 +723,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -763,7 +760,7 @@ "compare", "equality" ], - "time": "2014-05-11 23:00:21" + "time": "2014-11-16 21:32:38" }, { "name": "sebastian/diff", @@ -932,6 +929,57 @@ ], "time": "2014-09-10 00:51:36" }, + { + "name": "sebastian/global-state", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2014-10-06 09:23:50" + }, { "name": "sebastian/version", "version": "1.0.3", @@ -969,17 +1017,17 @@ }, { "name": "symfony/yaml", - "version": "v2.6.0", + "version": "v2.6.1", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8" + "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", - "reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20", + "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20", "shasum": "" }, "require": { @@ -1012,7 +1060,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-11-20 13:24:23" + "time": "2014-12-02 20:19:20" } ], "aliases": [], From e821ac2772541cbf5f26d84d036ed29f6e48f13f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 13 Dec 2014 23:56:15 +0100 Subject: [PATCH 556/638] Persist prefer-lowest in composer.lock --- doc/03-cli.md | 4 +++- src/Composer/Installer.php | 10 +++++++--- src/Composer/Package/Locker.php | 13 ++++++++++++- .../Fixtures/installer/install-dev-using-dist.test | 1 + .../Fixtures/installer/install-from-empty-lock.test | 3 ++- .../installer/install-missing-alias-from-lock.test | 3 ++- ...-update-downgrades-non-whitelisted-unstable.test | 2 ++ .../installer/partial-update-from-lock.test | 2 ++ .../installer/partial-update-without-lock.test | 1 + .../Test/Fixtures/installer/update-alias-lock.test | 4 +++- .../installer/update-whitelist-reads-lock.test | 3 ++- .../updating-dev-from-lock-removes-old-deps.test | 3 ++- .../updating-dev-updates-url-and-reference.test | 4 +++- tests/Composer/Test/Package/LockerTest.php | 5 +++-- 14 files changed, 45 insertions(+), 13 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 69f8f58c5..1541c31fe 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -140,7 +140,9 @@ php composer.phar update vendor/* * **--lock:** Only updates the lock file hash to suppress warning about the lock file being out of date. * **--with-dependencies** Add also all dependencies of whitelisted packages to the whitelist. - So all packages with their dependencies are updated recursively. +* **--prefer-stable:** Prefer stable versions of dependencies. +* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal + versions of requirements, generally used with `--prefer-stable`. ## require diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 58459f7a6..35df7e61b 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -309,7 +309,8 @@ class Installer $aliases, $this->package->getMinimumStability(), $this->package->getStabilityFlags(), - $this->package->getPreferStable() + $this->preferStable || $this->package->getPreferStable(), + $this->preferLowest ); if ($updatedLock) { $this->io->write('Writing lock file'); @@ -695,15 +696,18 @@ class Installer private function createPolicy() { - $preferLowest = false; $preferStable = null; + $preferLowest = null; if (!$this->update && $this->locker->isLocked()) { $preferStable = $this->locker->getPreferStable(); + $preferLowest = $this->locker->getPreferLowest(); } - // old lock file without prefer stable will return null + // old lock file without prefer stable/lowest will return null // so in this case we use the composer.json info if (null === $preferStable) { $preferStable = $this->preferStable || $this->package->getPreferStable(); + } + if (null === $preferLowest) { $preferLowest = $this->preferLowest; } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 9d49f6eaf..40ff8907c 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -182,6 +182,15 @@ class Locker return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null; } + public function getPreferLowest() + { + $lockData = $this->getLockData(); + + // return null if not set to allow caller logic to choose the + // right behavior since old lock files have no prefer-lowest + return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null; + } + public function getAliases() { $lockData = $this->getLockData(); @@ -213,10 +222,11 @@ class Locker * @param string $minimumStability * @param array $stabilityFlags * @param bool $preferStable + * @param bool $preferLowest * * @return bool */ - public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable) + public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest) { $lock = array( '_readme' => array('This file locks the dependencies of your project to a known state', @@ -229,6 +239,7 @@ class Locker 'minimum-stability' => $minimumStability, 'stability-flags' => $stabilityFlags, 'prefer-stable' => $preferStable, + 'prefer-lowest' => $preferLowest, ); foreach ($aliases as $package => $versions) { 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 af4eed811..ccc9ead1a 100644 --- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -48,6 +48,7 @@ install --prefer-dist "a/a": 20 }, "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test index 88c3e8fa7..7bb69f131 100644 --- a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test +++ b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test @@ -25,7 +25,8 @@ Requirements from the composer file are not installed if the lock file is presen "aliases": [], "minimum-stability": "stable", "stability-flags": [], - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --RUN-- install diff --git a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test index 298846609..5acb7a069 100644 --- a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test @@ -33,7 +33,8 @@ Installing an old alias that doesn't exist anymore from a lock is possible "aliases": [], "minimum-stability": "dev", "stability-flags": [], - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --RUN-- install diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test index f9fd5058a..4b2c62a53 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test @@ -36,6 +36,7 @@ Partial update from lock file should apply lock file and downgrade unstable pack "b/unstable": 15 }, "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } @@ -59,6 +60,7 @@ update c/uptodate "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test index 5b904f9b5..5c3508840 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test @@ -36,6 +36,7 @@ Partial update from lock file should update everything to the state of the lock, "b/unstable": 15 }, "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } @@ -59,6 +60,7 @@ update b/unstable "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test index 224e58f7d..e931a1f7d 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test @@ -43,6 +43,7 @@ update b/unstable "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test index bfe0bb8a8..920321a20 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test @@ -39,7 +39,8 @@ Update aliased package does not mess up the lock file "aliases": [], "minimum-stability": "dev", "stability-flags": [], - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --INSTALLED-- [ @@ -66,6 +67,7 @@ update "minimum-stability": "dev", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test index 3bc189015..96036e479 100644 --- a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test @@ -32,7 +32,8 @@ Limited update takes rules from lock if available, and not from the installed re "aliases": [], "minimum-stability": "stable", "stability-flags": [], - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --INSTALLED-- [ diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test index bd94617bc..04624561d 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test @@ -20,7 +20,8 @@ Installing locked dev packages should remove old dependencies "aliases": [], "minimum-stability": "dev", "stability-flags": [], - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --INSTALLED-- [ diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test index 849296850..c5c838517 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test @@ -32,7 +32,8 @@ Updating a dev package for new reference updates the url and reference "aliases": [], "minimum-stability": "dev", "stability-flags": {"a/a":20}, - "prefer-stable": false + "prefer-stable": false, + "prefer-lowest": false } --INSTALLED-- [ @@ -59,6 +60,7 @@ update "minimum-stability": "dev", "stability-flags": {"a/a":20}, "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 7e1d2d3b1..913868cd1 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -135,9 +135,10 @@ class LockerTest extends \PHPUnit_Framework_TestCase 'platform' => array(), 'platform-dev' => array(), 'prefer-stable' => false, + 'prefer-lowest' => false, )); - $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false); + $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false); } public function testLockBadPackages() @@ -156,7 +157,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $this->setExpectedException('LogicException'); - $locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false); + $locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false); } public function testIsFresh() From d621c51b2ce1c2e1781ffb44b15c2a76a5ee0069 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 14 Dec 2014 12:36:58 +0000 Subject: [PATCH 557/638] Bullet-proof global composer instantiation --- src/Composer/Factory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 8f7ba28e0..0eb428657 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -353,12 +353,12 @@ class Factory } $composer = null; - chdir($config->get('home')); try { + chdir($config->get('home')); $composer = self::createComposer($io, null, $disablePlugins, false); } catch (\Exception $e) { } - chdir($oldCwd); + @chdir($oldCwd); return $composer; } From 523aef76d0fa29c18bf42264e88fc9b58acf825c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 14 Dec 2014 13:46:37 +0000 Subject: [PATCH 558/638] Fix tests again.. --- tests/Composer/Test/CacheTest.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php index 0585e78b8..b01dbfd89 100644 --- a/tests/Composer/Test/CacheTest.php +++ b/tests/Composer/Test/CacheTest.php @@ -21,6 +21,10 @@ class CacheTest extends TestCase public function setUp() { + if (getenv('TRAVIS')) { + $this->markTestSkipped('Test causes intermittent failures on Travis'); + } + $this->root = sys_get_temp_dir() . '/composer_testdir'; $this->ensureDirectoryExistsAndClear($this->root); @@ -46,10 +50,6 @@ class CacheTest extends TestCase public function testRemoveOutdatedFiles() { - if (getenv('TRAVIS')) { - $this->markTestSkipped('Test causes intermittent failures on Travis'); - } - $outdated = array_slice($this->files, 1); $this->finder ->expects($this->once()) @@ -70,10 +70,6 @@ class CacheTest extends TestCase public function testRemoveFilesWhenCacheIsTooLarge() { - if (getenv('TRAVIS')) { - $this->markTestSkipped('Test causes intermittent failures on Travis'); - } - $emptyFinder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock(); $emptyFinder ->expects($this->once()) From 12ba415f0ed95fb53754c8a2afe74a8228e1796e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Sun, 14 Dec 2014 22:52:24 +0100 Subject: [PATCH 559/638] Fix documented Composer home variable name --- doc/04-schema.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 9799b4a5c..6dd06d635 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -758,13 +758,14 @@ The following options are supported: on how to get an OAuth token for GitHub. * **http-basic:** A list of domain names and username/passwords to authenticate against them. For example using - `{"example.org": {"username": "alice", "password": "foo"}` as the value of this option will let composer authenticate against example.org. + `{"example.org": {"username": "alice", "password": "foo"}` as the value of this + option will let composer authenticate against example.org. * **vendor-dir:** Defaults to `vendor`. You can install dependencies into a different directory if you want to. `$HOME` and `~` will be replaced by your home directory's path in vendor-dir and all `*-dir` options below. * **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they will be symlinked into this directory. -* **cache-dir:** Defaults to `$home/cache` on unix systems and +* **cache-dir:** Defaults to `$COMPOSER_HOME/cache` on unix systems and `C:\Users\\AppData\Local\Composer` on Windows. Stores all the caches used by composer. See also [COMPOSER_HOME](03-cli.md#composer-home). * **cache-files-dir:** Defaults to `$cache-dir/files`. Stores the zip archives From 47fd093a44fa116ea460dd65160737b8e0b46e8b Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Mon, 15 Dec 2014 08:43:07 +0100 Subject: [PATCH 560/638] added --no-progress to self-update closes composer/composer#3555 --- src/Composer/Command/SelfUpdateCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 13abee32b..0687de161 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -43,6 +43,7 @@ class SelfUpdateCommand extends Command new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'), new InputOption('clean-backups', null, InputOption::VALUE_NONE, 'Delete old backups during an update. This makes the current version of composer the only backup available after the update'), new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'), + new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), )) ->setHelp(<<self-update command checks getcomposer.org for newer @@ -105,7 +106,7 @@ EOT $output->writeln(sprintf("Updating to version %s.", $updateVersion)); $remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar"); - $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename); + $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress')); if (!file_exists($tempFilename)) { $output->writeln('The download of the new composer version failed for an unexpected reason'); From 0c87048fedea9de8c64530019eb81dd40882f278 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 15 Dec 2014 20:21:53 +0000 Subject: [PATCH 561/638] Also detect full temp dir, fixes #3559 --- src/Composer/Console/Application.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 8e49eedb5..1c8246f88 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -184,6 +184,7 @@ class Application extends BaseApplication $minSpaceFree = 1024*1024; if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree) || (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree) + || (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree) ) { $output->writeln('The disk hosting '.$dir.' is full, this may be the cause of the following exception'); } From 2b16a73659b12c35b2ab623b69688295d95ff2de Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 15 Dec 2014 20:34:23 +0000 Subject: [PATCH 562/638] Load plugins before purging packages, fixes #3557 --- src/Composer/Factory.php | 11 ++++++----- src/Composer/Installer/PluginInstaller.php | 4 ++-- src/Composer/Plugin/PluginManager.php | 7 +++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 0eb428657..d42c5e7dd 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -283,11 +283,6 @@ class Factory // add installers to the manager (must happen after download manager is created since they read it out of $composer) $this->createDefaultInstallers($im, $composer, $io); - // purge packages if they have been deleted on the filesystem - if ($rm->getLocalRepository()) { - $this->purgePackages($rm->getLocalRepository(), $im); - } - if ($fullLoad) { $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); $pm = $this->createPluginManager($io, $composer, $globalComposer); @@ -296,6 +291,12 @@ class Factory if (!$disablePlugins) { $pm->loadInstalledPlugins(); } + + // once we have plugins and custom installers we can + // purge packages from local repos if they have been deleted on the filesystem + if ($rm->getLocalRepository()) { + $this->purgePackages($rm->getLocalRepository(), $im); + } } // init locker if possible diff --git a/src/Composer/Installer/PluginInstaller.php b/src/Composer/Installer/PluginInstaller.php index b5469ccc6..ddb8e5bdd 100644 --- a/src/Composer/Installer/PluginInstaller.php +++ b/src/Composer/Installer/PluginInstaller.php @@ -61,7 +61,7 @@ class PluginInstaller extends LibraryInstaller } parent::install($repo, $package); - $this->composer->getPluginManager()->registerPackage($package); + $this->composer->getPluginManager()->registerPackage($package, true); } /** @@ -75,6 +75,6 @@ class PluginInstaller extends LibraryInstaller } parent::update($repo, $initial, $target); - $this->composer->getPluginManager()->registerPackage($target); + $this->composer->getPluginManager()->registerPackage($target, true); } } diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d7c2d8657..9e4c12391 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -190,10 +190,11 @@ class PluginManager * instead for BC * * @param PackageInterface $package + * @param bool $failOnMissingClasses By default this silently skips plugins that can not be found, but if set to true it fails with an exception * * @throws \UnexpectedValueException */ - public function registerPackage(PackageInterface $package) + public function registerPackage(PackageInterface $package, $failOnMissingClasses = false) { $oldInstallerPlugin = ($package->getType() === 'composer-installer'); @@ -242,10 +243,12 @@ class PluginManager if ($oldInstallerPlugin) { $installer = new $class($this->io, $this->composer); $this->composer->getInstallationManager()->addInstaller($installer); - } else { + } elseif (class_exists($class)) { $plugin = new $class(); $this->addPlugin($plugin); $this->registeredPlugins[] = $package->getName(); + } elseif ($failOnMissingClasses) { + throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class); } } } From 331bda235c810787be90219f7625cc8a548aa92e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 16 Dec 2014 11:12:13 +0000 Subject: [PATCH 563/638] Fix activation of global plugins, fixes #3557 --- src/Composer/Config.php | 23 +++++++++++++++++++++-- src/Composer/Factory.php | 24 ++++++++++++++---------- tests/Composer/Test/ConfigTest.php | 15 +++++++++++++++ tests/Composer/Test/Mock/FactoryMock.php | 4 ++-- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 176c228eb..4bb2a06c1 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -55,6 +55,7 @@ class Config ); private $config; + private $baseDir; private $repositories; private $configSource; private $authConfigSource; @@ -63,12 +64,13 @@ class Config /** * @param boolean $useEnvironment Use COMPOSER_ environment variables to replace config settings */ - public function __construct($useEnvironment = true) + public function __construct($useEnvironment = true, $baseDir = null) { // load defaults $this->config = static::$defaultConfig; $this->repositories = static::$defaultRepositories; $this->useEnvironment = (bool) $useEnvironment; + $this->baseDir = $baseDir; } public function setConfigSource(ConfigSourceInterface $source) @@ -167,7 +169,7 @@ class Config $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key]), '/\\'); $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val); - return $val; + return $this->realpath($val); case 'cache-ttl': return (int) $this->config[$key]; @@ -294,6 +296,23 @@ class Config }, $value); } + /** + * Turns relative paths in absolute paths without realpath() + * + * Since the dirs might not exist yet we can not call realpath or it will fail. + * + * @param string $path + * @return string + */ + private function realpath($path) + { + if (substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':') { + return $path; + } + + return $this->baseDir . '/' . $path; + } + /** * Reads the value of a Composer environment variable * diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index d42c5e7dd..f2aa27e22 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -89,8 +89,10 @@ class Factory * @param IOInterface|null $io * @return Config */ - public static function createConfig(IOInterface $io = null) + public static function createConfig(IOInterface $io = null, $cwd = null) { + $cwd = $cwd ?: getcwd(); + // determine home and cache dirs $home = self::getHomeDir(); $cacheDir = self::getCacheDir($home); @@ -107,7 +109,7 @@ class Factory } } - $config = new Config(); + $config = new Config(true, $cwd); // add dirs to the config $config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir))); @@ -192,8 +194,10 @@ class Factory * @throws \UnexpectedValueException * @return Composer */ - public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $fullLoad = true) + public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true) { + $cwd = $cwd ?: getcwd(); + // load Composer configuration if (null === $localConfig) { $localConfig = static::getComposerFile(); @@ -205,7 +209,7 @@ class Factory if (!$file->exists()) { if ($localConfig === './composer.json' || $localConfig === 'composer.json') { - $message = 'Composer could not find a composer.json file in '.getcwd(); + $message = 'Composer could not find a composer.json file in '.$cwd; } else { $message = 'Composer could not find the config file: '.$localConfig; } @@ -218,7 +222,7 @@ class Factory } // Load config and override with local config/auth config - $config = static::createConfig($io); + $config = static::createConfig($io, $cwd); $config->merge($localConfig); if (isset($composerFile)) { if ($io && $io->isDebug()) { @@ -348,18 +352,18 @@ class Factory */ protected function createGlobalComposer(IOInterface $io, Config $config, $disablePlugins) { - $oldCwd = getcwd(); - if (realpath($config->get('home')) === realpath($oldCwd)) { + if (realpath($config->get('home')) === getcwd()) { return; } $composer = null; try { - chdir($config->get('home')); - $composer = self::createComposer($io, null, $disablePlugins, false); + $composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false); } catch (\Exception $e) { + if ($io->isDebug()) { + $io->write('Failed to initialize global composer: '.$e->getMessage()); + } } - @chdir($oldCwd); return $composer; } diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 521fb634b..04160b63f 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -121,6 +121,21 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->assertEquals($home.'/foo', $config->get('cache-dir')); } + public function testRealpathReplacement() + { + $config = new Config(false, '/foo/bar'); + $config->merge(array('config' => array( + 'bin-dir' => '$HOME/foo', + 'cache-dir' => '/baz/', + 'vendor-dir' => 'vendor' + ))); + + $home = rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '\\/'); + $this->assertEquals('/foo/bar/vendor', $config->get('vendor-dir')); + $this->assertEquals($home.'/foo', $config->get('bin-dir')); + $this->assertEquals('/baz', $config->get('cache-dir')); + } + public function testOverrideGithubProtocols() { $config = new Config(false); diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php index ccbda8040..52c3fbf2e 100644 --- a/tests/Composer/Test/Mock/FactoryMock.php +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -22,9 +22,9 @@ use Composer\IO\IOInterface; class FactoryMock extends Factory { - public static function createConfig(IOInterface $io = null) + public static function createConfig(IOInterface $io = null, $cwd = null) { - $config = new Config(); + $config = new Config(true, $cwd); $config->merge(array( 'config' => array('home' => sys_get_temp_dir().'/composer-test'), From c54d6f93e3c5838092363447498ecb366e1e60a2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 16 Dec 2014 14:28:51 +0000 Subject: [PATCH 564/638] Add --absolute to resolve absolute paths in config command, otherwise set default back to relative --- doc/03-cli.md | 14 ++++++++------ src/Composer/Command/ConfigCommand.php | 3 ++- src/Composer/Config.php | 6 ++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 26e70f54a..9fc3d552a 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -397,16 +397,18 @@ options. ### Options * **--global (-g):** Operate on the global config file located at -`$COMPOSER_HOME/config.json` by default. Without this option, this command -affects the local composer.json file or a file specified by `--file`. + `$COMPOSER_HOME/config.json` by default. Without this option, this command + affects the local composer.json file or a file specified by `--file`. * **--editor (-e):** Open the local composer.json file using in a text editor as -defined by the `EDITOR` env variable. With the `--global` option, this opens -the global config file. + defined by the `EDITOR` env variable. With the `--global` option, this opens + the global config file. * **--unset:** Remove the configuration element named by `setting-key`. * **--list (-l):** Show the list of current config variables. With the `--global` - option this lists the global configuration only. + option this lists the global configuration only. * **--file="..." (-f):** Operate on a specific file instead of composer.json. Note - that this cannot be used in conjunction with the `--global` option. + that this cannot be used in conjunction with the `--global` option. +* **--absolute:** Returns absolute paths when fetching *-dir config values + instead of relative. ### Modifying Repositories diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index a08a6f4a6..832571b3f 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -57,6 +57,7 @@ class ConfigCommand extends Command new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'), new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'), new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json', 'composer.json'), + new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'), new InputArgument('setting-key', null, 'Setting key'), new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'), )) @@ -218,7 +219,7 @@ EOT $value = $data; } elseif (isset($data['config'][$settingKey])) { - $value = $data['config'][$settingKey]; + $value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS); } else { throw new \RuntimeException($settingKey.' is not defined'); } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 4bb2a06c1..f95b30177 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -19,6 +19,8 @@ use Composer\Config\ConfigSourceInterface; */ class Config { + const RELATIVE_PATHS = 1; + public static $defaultConfig = array( 'process-timeout' => 300, 'use-include-path' => false, @@ -153,7 +155,7 @@ class Config * @throws \RuntimeException * @return mixed */ - public function get($key) + public function get($key, $flags = 0) { switch ($key) { case 'vendor-dir': @@ -169,7 +171,7 @@ class Config $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key]), '/\\'); $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val); - return $this->realpath($val); + return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val); case 'cache-ttl': return (int) $this->config[$key]; From d22070526f4de7e12338e8c4baae2adb6c09e914 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 16 Dec 2014 15:52:34 +0100 Subject: [PATCH 565/638] Add the package license for PEAR packages Refs https://github.com/composer/satis/issues/192 --- src/Composer/Repository/PearRepository.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 5006c4927..6086df1ed 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -160,6 +160,7 @@ class PearRepository extends ArrayRepository $package = new CompletePackage($composerPackageName, $normalizedVersion, $version); $package->setType('pear-library'); $package->setDescription($packageDefinition->getDescription()); + $package->setLicense(array($packageDefinition->getLicense())); $package->setDistType('file'); $package->setDistUrl($distUrl); $package->setAutoload(array('classmap' => array(''))); From e0291f3a30fe8959884b433c47abfdb99797dd53 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 17 Dec 2014 15:25:22 +0000 Subject: [PATCH 566/638] Fix parsing of empty git commits, fixes #3565 --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index daa3d81d3..da87e2e7d 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -202,7 +202,7 @@ class GitDriver extends VcsDriver $this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir); foreach ($this->process->splitLines($output) as $branch) { if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) { - if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) { + if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match)) { $branches[$match[1]] = $match[2]; } } From 60ac971419ba4e8130b401757894b6b5636cf4e6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 17 Dec 2014 21:57:27 +0000 Subject: [PATCH 567/638] Fix relative path fetching when a var uses var replacement, fixes #3564 --- src/Composer/Config.php | 22 ++++++++++++++-------- tests/Composer/Test/ConfigTest.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index f95b30177..86e318765 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -152,6 +152,7 @@ class Config * Returns a setting * * @param string $key + * @param int $flags Options (see class constants) * @throws \RuntimeException * @return mixed */ @@ -168,9 +169,13 @@ class Config // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_')); - $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key]), '/\\'); + $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key], $flags), '/\\'); $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val); + if (substr($key, -4) !== '-dir') { + return $val; + } + return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val); case 'cache-ttl': @@ -207,7 +212,7 @@ class Config return (int) $this->config['cache-ttl']; case 'home': - return rtrim($this->process($this->config[$key]), '/\\'); + return rtrim($this->process($this->config[$key], $flags), '/\\'); case 'discard-changes': if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) { @@ -244,17 +249,17 @@ class Config return null; } - return $this->process($this->config[$key]); + return $this->process($this->config[$key], $flags); } } - public function all() + public function all($flags = 0) { $all = array( 'repositories' => $this->getRepositories(), ); foreach (array_keys($this->config) as $key) { - $all['config'][$key] = $this->get($key); + $all['config'][$key] = $this->get($key, $flags); } return $all; @@ -283,9 +288,10 @@ class Config * Replaces {$refs} inside a config string * * @param string $value a config string that can contain {$refs-to-other-config} + * @param int $flags Options (see class constants) * @return string */ - private function process($value) + private function process($value, $flags) { $config = $this; @@ -293,8 +299,8 @@ class Config return $value; } - return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config) { - return $config->get($match[1]); + return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config, $flags) { + return $config->get($match[1], $flags); }, $value); } diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 04160b63f..99970125c 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -136,6 +136,20 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->assertEquals('/baz', $config->get('cache-dir')); } + public function testFetchingRelativePaths() + { + $config = new Config(false, '/foo/bar'); + $config->merge(array('config' => array( + 'bin-dir' => '{$vendor-dir}/foo', + 'vendor-dir' => 'vendor' + ))); + + $this->assertEquals('/foo/bar/vendor', $config->get('vendor-dir')); + $this->assertEquals('/foo/bar/vendor/foo', $config->get('bin-dir')); + $this->assertEquals('vendor', $config->get('vendor-dir', Config::RELATIVE_PATHS)); + $this->assertEquals('vendor/foo', $config->get('bin-dir', Config::RELATIVE_PATHS)); + } + public function testOverrideGithubProtocols() { $config = new Config(false); From 6b5a531782fbfe837e0dcf7e5bc801e095fbcf00 Mon Sep 17 00:00:00 2001 From: tomzx Date: Wed, 17 Dec 2014 21:11:36 -0500 Subject: [PATCH 568/638] Minor fix to the HTTP Basic Authentication documentation Replaced JSON array with JSON object since you cannot assign key values in an array. --- doc/articles/http-basic-authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/articles/http-basic-authentication.md b/doc/articles/http-basic-authentication.md index af62fe2df..1add2d7a6 100644 --- a/doc/articles/http-basic-authentication.md +++ b/doc/articles/http-basic-authentication.md @@ -40,7 +40,7 @@ username/password pairs, for example: ```json { - "basic-auth": [ + "basic-auth": { "repo.example1.org": { "username": "my-username1", "password": "my-secret-password1" @@ -49,7 +49,7 @@ username/password pairs, for example: "username": "my-username2", "password": "my-secret-password2" } - ] + } } ``` From 70007fee4ef9d8e09f443de16fde096942a889b3 Mon Sep 17 00:00:00 2001 From: Arfan Date: Sun, 21 Dec 2014 02:18:29 +0530 Subject: [PATCH 569/638] Typo edited. Silly typo edit. --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 1ab4acbdb..d83e79aed 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -2,7 +2,7 @@ ## Installing -If you have not yet installed Composer, refer to to the [Intro](00-intro.md) chapter. +If you have not yet installed Composer, refer to the [Intro](00-intro.md) chapter. ## `composer.json`: Project Setup From 267af928df41085ef2390d686d245614bf2f6f80 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 22 Dec 2014 12:22:29 +0100 Subject: [PATCH 570/638] Add a failing testcase for stability flags in complex constraints Refs #3570 --- tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php index 51799d053..d1f3be1f8 100644 --- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php @@ -143,6 +143,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase 'foo/bar' => '~2.1.0-beta2', 'bar/baz' => '1.0.x-dev as 1.2.0', 'qux/quux' => '1.0.*@rc', + 'zux/complex' => '~1.0,>=1.0.2@dev' ), 'minimum-stability' => 'alpha', )); @@ -151,6 +152,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array( 'bar/baz' => BasePackage::STABILITY_DEV, 'qux/quux' => BasePackage::STABILITY_RC, + 'zux/complex' => BasePackage::STABILITY_DEV, ), $package->getStabilityFlags()); } } From d8813341c3fb0026c130f2d4e153e4954906f861 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 22 Dec 2014 12:31:34 +0100 Subject: [PATCH 571/638] Support parsing stability flags on complex constraints Fixes #3570 --- src/Composer/Package/Loader/RootPackageLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 64880d727..250f2952f 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -131,7 +131,7 @@ class RootPackageLoader extends ArrayLoader $minimumStability = $stabilities[$minimumStability]; foreach ($requires as $reqName => $reqVersion) { // parse explicit stability flags to the most unstable - if (preg_match('{^[^,\s]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) { + if (preg_match('{^[^@]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) { $name = strtolower($reqName); $stability = $stabilities[VersionParser::normalizeStability($match[1])]; From c93305a80548bf04650d8c05c98803b95dada90e Mon Sep 17 00:00:00 2001 From: SofHad Date: Fri, 26 Dec 2014 00:07:49 +0100 Subject: [PATCH 572/638] To show instead of open the repository or homepage URL --- src/Composer/Command/HomeCommand.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 5b0e32244..fe7929839 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -40,12 +40,14 @@ class HomeCommand extends Command ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Package(s) to browse to.'), new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'), + new InputOption('show', 's', InputOption::VALUE_NONE, 'Only show the homepage or repository URL.'), )) ->setHelp(<<openBrowser($url); + if ($input->getOption('show')) { + $output->writeln(sprintf('%s: %s', $input->getOption('homepage') ? 'Homepage URL' : 'Repository URL', $url).' '); + } else { + $this->openBrowser($url); + } } return $return; From 9bc6209be931e423b6377a1502c093578b8ba9ab Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 16 Dec 2014 18:45:36 +0100 Subject: [PATCH 573/638] Fix tests for the pear repository --- tests/Composer/Test/Repository/Pear/ChannelReaderTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php index 214d7b702..c22250a04 100644 --- a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php +++ b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php @@ -121,6 +121,7 @@ class ChannelReaderTest extends TestCase $expectedPackage->setType('pear-library'); $expectedPackage->setDistType('file'); $expectedPackage->setDescription('description'); + $expectedPackage->setLicense(array('license')); $expectedPackage->setDistUrl("http://test.loc/get/sample-1.0.0.1.tgz"); $expectedPackage->setAutoload(array('classmap' => array(''))); $expectedPackage->setIncludePaths(array('/')); From 45089a6771e00c644cb543372def3d6eceb301b6 Mon Sep 17 00:00:00 2001 From: SofHad Date: Sun, 28 Dec 2014 00:42:01 +0100 Subject: [PATCH 574/638] [Minor] remove the unused private variables --- src/Composer/Repository/ComposerRepository.php | 2 -- src/Composer/Util/RemoteFilesystem.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index d5d8c11ef..5409a8dd3 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -53,8 +53,6 @@ class ComposerRepository extends ArrayRepository protected $eventDispatcher; protected $sourceMirrors; protected $distMirrors; - private $rawData; - private $minimalPackages; private $degradedMode = false; private $rootData; diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index cfb4a946e..e03cf8ec8 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -26,7 +26,6 @@ class RemoteFilesystem { private $io; private $config; - private $firstCall; private $bytesMax; private $originUrl; private $fileUrl; From 82b84f7a0ac1e575b95d1540ea8c6c8cab3392cc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 29 Dec 2014 19:27:35 +0000 Subject: [PATCH 575/638] Fix formatting of constraint output --- src/Composer/Package/LinkConstraint/MultiConstraint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/LinkConstraint/MultiConstraint.php b/src/Composer/Package/LinkConstraint/MultiConstraint.php index f2eff93ec..3871aeb2f 100644 --- a/src/Composer/Package/LinkConstraint/MultiConstraint.php +++ b/src/Composer/Package/LinkConstraint/MultiConstraint.php @@ -78,6 +78,6 @@ class MultiConstraint implements LinkConstraintInterface $constraints[] = $constraint->__toString(); } - return '['.implode($this->conjunctive ? ', ' : ' | ', $constraints).']'; + return '['.implode($this->conjunctive ? ' ' : ' || ', $constraints).']'; } } From 095dc6129550de93cd98170c8e6c6ccd4558e983 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 29 Dec 2014 20:29:13 +0000 Subject: [PATCH 576/638] Rename runAutoloader to dumpAutoloader, refs #3453 --- src/Composer/Command/InstallCommand.php | 2 +- src/Composer/Command/UpdateCommand.php | 2 +- src/Composer/Installer.php | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index c1de25ce7..98cd21b9f 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -114,7 +114,7 @@ EOT ->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) - ->setRunAutoloader(!$input->getOption('no-autoloader')) + ->setDumpAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index cb3b50209..10fa26987 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -118,7 +118,7 @@ EOT ->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) - ->setRunAutoloader(!$input->getOption('no-autoloader')) + ->setDumpAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($optimize) ->setUpdate(true) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 041a2f6c9..3594336e3 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -105,7 +105,7 @@ class Installer protected $dryRun = false; protected $verbose = false; protected $update = false; - protected $runAutoloader = true; + protected $dumpAutoloader = true; protected $runScripts = true; protected $ignorePlatformReqs = false; protected $preferStable = false; @@ -318,14 +318,14 @@ class Installer } } - if ($this->runAutoloader) { + if ($this->dumpAutoloader) { // write autoloader if ($this->optimizeAutoloader) { $this->io->write('Generating optimized autoload files'); } else { $this->io->write('Generating autoload files'); } - + $this->autoloadGenerator->setDevMode($this->devMode); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } @@ -1169,14 +1169,14 @@ class Installer /** * set whether to run autoloader or not - * - * @param boolean $runAutoloader + * + * @param boolean $dumpAutoloader * @return Installer */ - public function setRunAutoloader($runAutoloader = true) + public function setDumpAutoloader($dumpAutoloader = true) { - $this->runAutoloader = (boolean) $runAutoloader; - + $this->dumpAutoloader = (boolean) $dumpAutoloader; + return $this; } From 5c43485e570dd5cf7594dabb3edc6257896f872e Mon Sep 17 00:00:00 2001 From: SofHad Date: Sun, 28 Dec 2014 00:53:35 +0100 Subject: [PATCH 577/638] Suppress unused parameters in HomeCommand --- src/Composer/Command/HomeCommand.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 5b0e32244..09b48bb2b 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -55,7 +55,7 @@ EOT */ protected function execute(InputInterface $input, OutputInterface $output) { - $repo = $this->initializeRepo($input, $output); + $repo = $this->initializeRepo(); $return = 0; foreach ($input->getArgument('packages') as $packageName) { @@ -138,13 +138,11 @@ EOT } /** - * initializes the repo + * Initializes the repo * - * @param InputInterface $input - * @param OutputInterface $output * @return CompositeRepository */ - private function initializeRepo(InputInterface $input, OutputInterface $output) + private function initializeRepo() { $composer = $this->getComposer(false); From 8d0c1a14b33a107712b6ced6857d77eef685ba48 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 1 Jan 2015 16:21:22 +0000 Subject: [PATCH 578/638] Add --ignore-platform-reqs to create-project, refs #1426 --- doc/03-cli.md | 3 +++ src/Composer/Command/CreateProjectCommand.php | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index ad31c2220..f9625eb2e 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -467,6 +467,9 @@ By default the command checks for the packages on packagist.org. * **--keep-vcs:** Skip the deletion of the VCS metadata for the created project. This is mostly useful if you run the command in non-interactive mode. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and force the installation even if the local machine does not + fulfill these. ## dump-autoload diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 4a3d33831..0242afcd3 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -69,6 +69,7 @@ class CreateProjectCommand extends Command new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deletion vcs folder.'), new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), )) ->setHelp(<<create-project command creates a new project from a given @@ -125,11 +126,12 @@ EOT $input->getOption('keep-vcs'), $input->getOption('no-progress'), $input->getOption('no-install'), + $input->getOption('ignore-platform-reqs'), $input ); } - public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, InputInterface $input) + public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, InputInterface $input) { $oldCwd = getcwd(); @@ -159,7 +161,8 @@ EOT $installer->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode($installDevPackages) - ->setRunScripts( ! $noScripts); + ->setRunScripts(!$noScripts) + ->setIgnorePlatformRequirements($ignorePlatformReqs); if ($disablePlugins) { $installer->disablePlugins(); From 220bd2bd1edc19c809a341f8f1e1dfac15ccacff Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 1 Jan 2015 16:26:14 +0000 Subject: [PATCH 579/638] Remove phpunit repo from tests --- tests/Composer/Test/Repository/PearRepositoryTest.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/Composer/Test/Repository/PearRepositoryTest.php b/tests/Composer/Test/Repository/PearRepositoryTest.php index a42c8e0b3..7eaad13e7 100644 --- a/tests/Composer/Test/Repository/PearRepositoryTest.php +++ b/tests/Composer/Test/Repository/PearRepositoryTest.php @@ -84,13 +84,6 @@ class PearRepositoryTest extends TestCase public function repositoryDataProvider() { return array( - array( - 'pear.phpunit.de', - array( - array('name' => 'pear-pear.phpunit.de/PHPUnit_MockObject', 'version' => '1.1.1'), - array('name' => 'pear-pear.phpunit.de/PHPUnit', 'version' => '3.6.10'), - ) - ), array( 'pear.php.net', array( From bf32d2b83ce52a1cf79e4ee7310e405bf92f1eae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 1 Jan 2015 20:53:06 +0000 Subject: [PATCH 580/638] Update deps --- composer.json | 2 +- composer.lock | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index 6d239970d..9fca7d74c 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.3.2", - "justinrainbow/json-schema": "~1.1", + "justinrainbow/json-schema": "~1.3", "seld/jsonlint": "~1.0", "symfony/console": "~2.3", "symfony/finder": "~2.2", diff --git a/composer.lock b/composer.lock index 0c7ff85ba..7d28fcb09 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "0430383b5ba00e406ce4a44253f49fe7", + "hash": "2bc9cc8aa706b68d611d7058e4eb8de7", "packages": [ { "name": "justinrainbow/json-schema", @@ -327,16 +327,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.0.13", + "version": "2.0.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5" + "reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5", - "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca158276c1200cc27f5409a5e338486bc0b4fc94", + "reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94", "shasum": "" }, "require": { @@ -388,7 +388,7 @@ "testing", "xunit" ], - "time": "2014-12-03 06:41:44" + "time": "2014-12-26 13:28:33" }, { "name": "phpunit/php-file-iterator", @@ -574,16 +574,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.4.0", + "version": "4.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0" + "reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0", - "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a5e49a86ce5e33b8d0657abe145057fc513543a", + "reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a", "shasum": "" }, "require": { @@ -641,7 +641,7 @@ "testing", "xunit" ], - "time": "2014-12-05 06:49:03" + "time": "2014-12-28 07:57:05" }, { "name": "phpunit/phpunit-mock-objects", @@ -982,16 +982,16 @@ }, { "name": "sebastian/version", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + "reference": "a77d9123f8e809db3fbdea15038c27a95da4058b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", - "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/a77d9123f8e809db3fbdea15038c27a95da4058b", + "reference": "a77d9123f8e809db3fbdea15038c27a95da4058b", "shasum": "" }, "type": "library", @@ -1013,7 +1013,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2014-03-07 15:35:33" + "time": "2014-12-15 14:25:24" }, { "name": "symfony/yaml", @@ -1067,6 +1067,7 @@ "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": { "php": ">=5.3.2" }, From e172cd81a146666d844d2b7b37b5feccb89e2b61 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 2 Jan 2015 07:52:56 +0000 Subject: [PATCH 581/638] Fix lax validation of packages when loading them, fixes #3606, fixes #3605 --- src/Composer/Json/JsonFile.php | 3 +-- src/Composer/Json/JsonValidationException.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index ceaffaa25..3375329c7 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -154,8 +154,7 @@ class JsonFile if ($schema === self::LAX_SCHEMA) { $schemaData->additionalProperties = true; - $schemaData->properties->name->required = false; - $schemaData->properties->description->required = false; + $schemaData->required = array(); } $validator = new Validator(); diff --git a/src/Composer/Json/JsonValidationException.php b/src/Composer/Json/JsonValidationException.php index 0b2b2ba70..5d21f533c 100644 --- a/src/Composer/Json/JsonValidationException.php +++ b/src/Composer/Json/JsonValidationException.php @@ -21,10 +21,10 @@ class JsonValidationException extends Exception { protected $errors; - public function __construct($message, $errors = array()) + public function __construct($message, $errors = array(), \Exception $previous = null) { $this->errors = $errors; - parent::__construct($message); + parent::__construct($message, 0, $previous); } public function getErrors() From ad1f8e6c5afdf869754c9d7092c3bf8d33e8d1ed Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Sat, 3 Jan 2015 17:35:25 -0700 Subject: [PATCH 582/638] Add classmap-authoritative config setting Add a "classmap-authoritative" configuration setting that can be used to disable searching the various prefix and fallback directories for classes that have not been registered with the Composer\Autoload\ClassLoader class map. This setting can be used to optimize performance by avoiding a potentially large number of `file_exists` calls when Composer is being used in a program with additional autoloader facilities. Use of the setting implies "optimize-autoloader" to ensure that the most complete class map possible is generated. Closes #3603 --- doc/04-schema.md | 3 + res/composer-schema.json | 4 + src/Composer/Autoload/AutoloadGenerator.php | 12 ++- src/Composer/Autoload/ClassLoader.php | 25 ++++++ src/Composer/Command/ConfigCommand.php | 1 + src/Composer/Command/DumpAutoloadCommand.php | 2 +- src/Composer/Command/InstallCommand.php | 2 +- src/Composer/Command/UpdateCommand.php | 2 +- src/Composer/Config.php | 1 + .../Test/Autoload/AutoloadGeneratorTest.php | 76 +++++++++++++++---- 10 files changed, 110 insertions(+), 18 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 6dd06d635..f88ce11df 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -789,6 +789,9 @@ The following options are supported: the generated Composer autoloader. When null a random one will be generated. * **optimize-autoloader** Defaults to `false`. Always optimize when dumping the autoloader. +* **classmap-authoritative:** Defaults to `false`. If true, the composer + autoloader will not scan the filesystem for classes that are not found in + the class map. Implies 'optimize-autoloader'. * **github-domains:** Defaults to `["github.com"]`. A list of domains to use in github mode. This is used for GitHub Enterprise setups. * **github-expose-hostname:** Defaults to `true`. If set to false, the OAuth diff --git a/res/composer-schema.json b/res/composer-schema.json index 49ec0fa7a..4c40bdfb2 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -197,6 +197,10 @@ "type": "boolean", "description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true." }, + "classmap-authoritative": { + "type": "boolean", + "description": "If true, the composer autoloader will not scan the filesystem for classes that are not found in the class map, defaults to false." + }, "github-domains": { "type": "array", "description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 19f194fa1..c88c34b41 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -63,6 +63,7 @@ class AutoloadGenerator $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir'))); $useGlobalIncludePath = (bool) $config->get('use-include-path'); $prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true'; + $classMapAuthoritative = $config->get('classmap-authoritative'); $targetDir = $vendorPath.'/'.$targetDir; $filesystem->ensureDirectoryExists($targetDir); @@ -226,7 +227,7 @@ EOF; file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile); } file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)); + file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative)); // use stream_copy_to_stream instead of copy // to work around https://bugs.php.net/bug.php?id=64634 @@ -443,7 +444,7 @@ return ComposerAutoloaderInit$suffix::getLoader(); AUTOLOAD; } - protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader) + protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative) { // TODO the class ComposerAutoloaderInit should be revert to a closure // when APC has been fixed: @@ -520,6 +521,13 @@ PSR4; CLASSMAP; } + if ($classMapAuthoritative) { + $file .= <<<'CLASSMAPAUTHORITATIVE' + $loader->setClassMapAuthoritative(true); + +CLASSMAPAUTHORITATIVE; + } + if ($useGlobalIncludePath) { $file .= <<<'INCLUDEPATH' $loader->setUseIncludePath(true); diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 70d78bc3f..112815324 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -54,6 +54,8 @@ class ClassLoader private $useIncludePath = false; private $classMap = array(); + private $classMapAuthoratative = false; + public function getPrefixes() { if (!empty($this->prefixesPsr0)) { @@ -248,6 +250,27 @@ class ClassLoader return $this->useIncludePath; } + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoratative + */ + public function setClassMapAuthoritative($classMapAuthoratative) + { + $this->classMapAuthoratative = $classMapAuthoratative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function getClassMapAuthoratative() + { + return $this->classMapAuthoratative; + } + /** * Registers this instance as an autoloader. * @@ -298,6 +321,8 @@ class ClassLoader // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; + } elseif ($this->classMapAuthoratative) { + return false; } $file = $this->findFileWithExtension($class, '.php'); diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 832571b3f..b50707f0b 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -323,6 +323,7 @@ EOT ), 'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }), 'optimize-autoloader' => array($booleanValidator, $booleanNormalizer), + 'classmap-authoritative' => array($booleanValidator, $booleanNormalizer), 'prepend-autoloader' => array($booleanValidator, $booleanNormalizer), 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer), ); diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index b30fbd140..adcc7adfd 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -52,7 +52,7 @@ EOT $package = $composer->getPackage(); $config = $composer->getConfig(); - $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader'); + $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative'); if ($optimize) { $output->writeln('Generating optimized autoload files'); diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 98cd21b9f..9d359cad0 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -106,7 +106,7 @@ EOT $preferDist = $input->getOption('prefer-dist'); } - $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); + $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative'); $install ->setDryRun($input->getOption('dry-run')) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 10fa26987..4e4f003c9 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -110,7 +110,7 @@ EOT $preferDist = $input->getOption('prefer-dist'); } - $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); + $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative'); $install ->setDryRun($input->getOption('dry-run')) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 86e318765..92ce8246b 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -39,6 +39,7 @@ class Config 'discard-changes' => false, 'autoloader-suffix' => null, 'optimize-autoloader' => false, + 'classmap-authoritative' => false, 'prepend-autoloader' => true, 'github-domains' => array('github.com'), 'github-expose-hostname' => true, diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 7c4ddccd9..7ca90b244 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -67,6 +67,11 @@ class AutoloadGeneratorTest extends TestCase */ private $eventDispatcher; + /** + * @var array + */ + private $configValueMap; + protected function setUp() { $this->fs = new Filesystem; @@ -79,18 +84,23 @@ class AutoloadGeneratorTest extends TestCase $this->config = $this->getMock('Composer\Config'); - $this->config->expects($this->at(0)) - ->method('get') - ->with($this->equalTo('vendor-dir')) - ->will($this->returnCallback(function () use ($that) { + $this->configValueMap = array( + 'vendor-dir' => function () use ($that) { return $that->vendorDir; - })); + }, + ); - $this->config->expects($this->at(1)) + $this->config->expects($this->atLeastOnce()) ->method('get') - ->with($this->equalTo('vendor-dir')) - ->will($this->returnCallback(function () use ($that) { - return $that->vendorDir; + ->will($this->returnCallback(function ($arg) use ($that) { + $ret = null; + if (isset($that->configValueMap[$arg])) { + $ret = $that->configValueMap[$arg]; + if (is_callable($ret)) { + $ret = $ret(); + } + } + return $ret; })); $this->origDir = getcwd(); @@ -483,6 +493,49 @@ class AutoloadGeneratorTest extends TestCase include $this->vendorDir.'/composer/autoload_classmap.php' ); $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); + $this->assertNotRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + } + + public function testClassMapAutoloadingAuthoritative() + { + $package = new Package('a', '1.0', '1.0'); + + $packages = array(); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + $packages[] = $c = new Package('c/c', '1.0', '1.0'); + $a->setAutoload(array('classmap' => array(''))); + $b->setAutoload(array('classmap' => array('test.php'))); + $c->setAutoload(array('classmap' => array('./'))); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue($packages)); + + $this->configValueMap['classmap-authoritative'] = true; + + $this->fs->ensureDirectoryExists($this->vendorDir.'/composer'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/c/c/foo'); + file_put_contents($this->vendorDir.'/a/a/src/a.php', 'vendorDir.'/b/b/test.php', 'vendorDir.'/c/c/foo/test.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_7'); + $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated."); + $this->assertEquals( + array( + 'ClassMapBar' => $this->vendorDir.'/b/b/test.php', + 'ClassMapBaz' => $this->vendorDir.'/c/c/foo/test.php', + 'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php', + ), + include $this->vendorDir.'/composer/autoload_classmap.php' + ); + $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); + + $this->assertRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + // FIXME: how can we actually test the ClassLoader implementation? } public function testFilesAutoloadGeneration() @@ -829,10 +882,7 @@ EOF; ->method('getCanonicalPackages') ->will($this->returnValue(array())); - $this->config->expects($this->at(2)) - ->method('get') - ->with($this->equalTo('use-include-path')) - ->will($this->returnValue(true)); + $this->configValueMap['use-include-path'] = true; $this->fs->ensureDirectoryExists($this->vendorDir.'/a'); From 92455759fc6e74b5e8eefab1d17fef118876cbe6 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Sat, 3 Jan 2015 18:01:18 -0700 Subject: [PATCH 583/638] Fix AutoloadGeneratorTest for PHP 5.3.x --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 7ca90b244..54370381c 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -68,9 +68,15 @@ class AutoloadGeneratorTest extends TestCase private $eventDispatcher; /** + * Map of setting name => return value configuration for the stub Config + * object. + * + * Note: must be public for compatibility with PHP 5.3 runtimes where + * closures cannot access private members of the classes they are created + * in. * @var array */ - private $configValueMap; + public $configValueMap; protected function setUp() { From e0d36e19eb8a5c96ccad3d145faf9024727e856c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 5 Jan 2015 16:17:15 +0000 Subject: [PATCH 584/638] Disable overwrites when no-ansi is present, fixes #3612 --- src/Composer/IO/ConsoleIO.php | 8 ++++++++ tests/Composer/Test/IO/ConsoleIOTest.php | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 07e529666..491a6b7fb 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -113,6 +113,14 @@ class ConsoleIO extends BaseIO */ public function overwrite($messages, $newline = true, $size = null) { + if (!$this->output->isDecorated()) { + if (!$messages) { + return; + } + + return $this->write($messages, count($messages) === 1 || $newline); + } + // messages can be an array, let's convert it to string anyway $messages = join($newline ? "\n" : '', (array) $messages); diff --git a/tests/Composer/Test/IO/ConsoleIOTest.php b/tests/Composer/Test/IO/ConsoleIOTest.php index 3a4313f69..6ce95b0ac 100644 --- a/tests/Composer/Test/IO/ConsoleIOTest.php +++ b/tests/Composer/Test/IO/ConsoleIOTest.php @@ -58,21 +58,27 @@ class ConsoleIOTest extends TestCase ->method('write') ->with($this->equalTo('something (strlen = 23)')); $outputMock->expects($this->at(1)) - ->method('write') - ->with($this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false)); + ->method('isDecorated') + ->willReturn(true); $outputMock->expects($this->at(2)) ->method('write') - ->with($this->equalTo('shorter (12)'), $this->equalTo(false)); + ->with($this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false)); $outputMock->expects($this->at(3)) ->method('write') - ->with($this->equalTo(str_repeat(' ', 11)), $this->equalTo(false)); + ->with($this->equalTo('shorter (12)'), $this->equalTo(false)); $outputMock->expects($this->at(4)) ->method('write') - ->with($this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false)); + ->with($this->equalTo(str_repeat(' ', 11)), $this->equalTo(false)); $outputMock->expects($this->at(5)) ->method('write') - ->with($this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false)); + ->with($this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false)); $outputMock->expects($this->at(6)) + ->method('isDecorated') + ->willReturn(true); + $outputMock->expects($this->at(7)) + ->method('write') + ->with($this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false)); + $outputMock->expects($this->at(8)) ->method('write') ->with($this->equalTo('something longer than initial (34)')); From c58b7d917c65692eeb00c22b2fbaaa251cb390dc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 5 Jan 2015 16:31:16 +0000 Subject: [PATCH 585/638] Disable progress when no-ansi is specified, refs #3612 --- src/Composer/Command/Command.php | 14 ++++++++++++++ src/Composer/Command/ConfigCommand.php | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/Composer/Command/Command.php b/src/Composer/Command/Command.php index b99686219..6c5226c6a 100644 --- a/src/Composer/Command/Command.php +++ b/src/Composer/Command/Command.php @@ -16,6 +16,8 @@ use Composer\Composer; use Composer\Console\Application; use Composer\IO\IOInterface; use Composer\IO\NullIO; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Command\Command as BaseCommand; /** @@ -102,4 +104,16 @@ abstract class Command extends BaseCommand { $this->io = $io; } + + /** + * {@inheritDoc} + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--no-ansi')) && $input->hasOption('no-progress')) { + $input->setOption('no-progress', true); + } + + parent::initialize($input, $output); + } } diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 832571b3f..4d52c8473 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -100,6 +100,8 @@ EOT */ protected function initialize(InputInterface $input, OutputInterface $output) { + parent::initialize($input, $output); + if ($input->getOption('global') && 'composer.json' !== $input->getOption('file')) { throw new \RuntimeException('--file and --global can not be combined'); } From 8ceb5714a49e848b549cbbc2ff9f6a496f4e3618 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Mon, 5 Jan 2015 11:48:34 -0700 Subject: [PATCH 586/638] Fix spelling of "Authoritative" - classMapAuthoratative -> classMapAuthoritative - getClassMapAuthoratative -> isClassMapAuthoritative - Don't use elseif since if block returns --- src/Composer/Autoload/ClassLoader.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 112815324..5e1469e83 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -54,7 +54,7 @@ class ClassLoader private $useIncludePath = false; private $classMap = array(); - private $classMapAuthoratative = false; + private $classMapAuthoritative = false; public function getPrefixes() { @@ -254,11 +254,11 @@ class ClassLoader * Turns off searching the prefix and fallback directories for classes * that have not been registered with the class map. * - * @param bool $classMapAuthoratative + * @param bool $classMapAuthoritative */ - public function setClassMapAuthoritative($classMapAuthoratative) + public function setClassMapAuthoritative($classMapAuthoritative) { - $this->classMapAuthoratative = $classMapAuthoratative; + $this->classMapAuthoritative = $classMapAuthoritative; } /** @@ -266,9 +266,9 @@ class ClassLoader * * @return bool */ - public function getClassMapAuthoratative() + public function isClassMapAuthoritative() { - return $this->classMapAuthoratative; + return $this->classMapAuthoritative; } /** @@ -321,7 +321,8 @@ class ClassLoader // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; - } elseif ($this->classMapAuthoratative) { + } + if ($this->classMapAuthoritative) { return false; } From 0f2558dab3d893a3ab72c6c39377ec5bd5e47763 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Mon, 5 Jan 2015 12:05:17 -0700 Subject: [PATCH 587/638] Use assertContains in ClassMapAuthoritative checks Use assertContains instead of assertRegExp when checking for ClassMapAuthoritative settings. --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 54370381c..2ad1e4729 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -499,7 +499,7 @@ class AutoloadGeneratorTest extends TestCase include $this->vendorDir.'/composer/autoload_classmap.php' ); $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); - $this->assertNotRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertNotContains('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } public function testClassMapAutoloadingAuthoritative() @@ -540,8 +540,7 @@ class AutoloadGeneratorTest extends TestCase ); $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); - $this->assertRegExp('/\$loader->setClassMapAuthoritative\(true\);/', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); - // FIXME: how can we actually test the ClassLoader implementation? + $this->assertContains('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } public function testFilesAutoloadGeneration() From 5b800169e757ad28e8e8a19f64c70b7c761226ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 7 Jan 2015 21:47:00 +0100 Subject: [PATCH 588/638] move contributing instructions to a separate file A link to the CONTRIBUTING.md will show up on Github's pull request creation form. Plus, this makes the README shorter and more relevant for users. --- CONTRIBUTING.md | 29 +++++++++++++++++++++++++++++ README.md | 26 -------------------------- 2 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..820a4002e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +Contributing to Composer +======================== + +Installation from Source +------------------------ + +Prior to contributing to Composer, you must use be able to run the tests. +To achieve this, you must use the sources and not the phar file. + +1. Run `git clone https://github.com/composer/composer.git` +2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable +3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install` + +You can now run Composer by executing the `bin/composer` script: `php /path/to/composer/bin/composer` + +Contributing policy +------------------- + +All code contributions - including those of people having commit access - +must go through a pull request and approved by a core developer before being +merged. This is to ensure proper review of all the code. + +Fork the project, create a feature branch, and send us a pull request. + +To ensure a consistent code base, you should make sure the code follows +the [Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html) +which we borrowed from Symfony. + +If you would like to help, take a look at the [list of issues](http://github.com/composer/composer/issues). diff --git a/README.md b/README.md index a86bc83f6..728e11f2e 100644 --- a/README.md +++ b/README.md @@ -32,18 +32,6 @@ themselves. To create libraries/packages please read the 3. Run Composer: `php composer.phar install` 4. Browse for more packages on [Packagist](https://packagist.org). -Installation from Source ------------------------- - -To run tests, or develop Composer itself, you must use the sources and not the phar -file as described above. - -1. Run `git clone https://github.com/composer/composer.git` -2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable -3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install` - -You can now run Composer by executing the `bin/composer` script: `php /path/to/composer/bin/composer` - Global installation of Composer (manual) ---------------------------------------- @@ -55,20 +43,6 @@ Updating Composer Running `php composer.phar self-update` or equivalent will update a phar install with the latest version. -Contributing ------------- - -All code contributions - including those of people having commit access - -must go through a pull request and approved by a core developer before being -merged. This is to ensure proper review of all the code. - -Fork the project, create a feature branch, and send us a pull request. - -To ensure a consistent code base, you should make sure the code follows -the [Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html) -which we borrowed from Symfony. - -If you would like to help take a look at the [list of issues](http://github.com/composer/composer/issues). Community --------- From 091fee8a3b3b0e3a9a18c7a681797038c018cbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Thu, 8 Jan 2015 08:56:12 +0100 Subject: [PATCH 589/638] skip some tests when the zip extension misses It is required for some tests to run. --- tests/Composer/Test/Repository/ArtifactRepositoryTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php index 7d64a0141..66c02acdc 100644 --- a/tests/Composer/Test/Repository/ArtifactRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArtifactRepositoryTest.php @@ -19,6 +19,14 @@ use Composer\Package\BasePackage; class ArtifactRepositoryTest extends TestCase { + public function setUp() + { + parent::setUp(); + if (!extension_loaded('zip')) { + $this->markTestSkipped('You need the zip extension to run this test.'); + } + } + public function testExtractsConfigsFromZipArchives() { $expectedPackages = array( From 200f867d64d13fbc25648ef9f538c2cc7869a076 Mon Sep 17 00:00:00 2001 From: Hannes Van De Vreken Date: Fri, 9 Jan 2015 10:08:13 +0100 Subject: [PATCH 590/638] "suggest" was on a wrong heading level compared to the others --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 6dd06d635..8662ca855 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -375,7 +375,7 @@ useful for common interfaces. A package could depend on some virtual `logger` package, any library that implements this logger interface would simply list it in `provide`. -### suggest +#### suggest Suggested packages that can enhance or work well with this package. These are just informational and are displayed after the package is installed, to give From 7e3f4805c0670ff3dbbff40a4d4e5f4f66b73adc Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 10 Jan 2015 17:25:31 +0100 Subject: [PATCH 591/638] Updated the documentation of the require command It did not mention that it is possible to leave out the version constraint. --- src/Composer/Command/RequireCommand.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 91043ad71..ea972aaf0 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -38,7 +38,7 @@ class RequireCommand extends InitCommand ->setName('require') ->setDescription('Adds required packages to your composer.json and installs them') ->setDefinition(array( - new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Required package with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), + new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Required package name optionally including a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), @@ -50,7 +50,9 @@ class RequireCommand extends InitCommand new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), )) ->setHelp(<< Date: Sun, 11 Jan 2015 23:09:41 +0000 Subject: [PATCH 592/638] Also clean up DYLD_LIBRARY_PATH before running git, refs #2146 --- src/Composer/Util/Git.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 4a0c3933f..c3c5eb02c 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -169,6 +169,9 @@ class Git if (getenv('GIT_WORK_TREE')) { putenv('GIT_WORK_TREE'); } + + // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940 + putenv("DYLD_LIBRARY_PATH"); } public static function getGitHubDomainsRegex(Config $config) From 9f0d36290bbbb81d0063225da4f8949d0fefc12c Mon Sep 17 00:00:00 2001 From: Vladimir Kartaviy Date: Mon, 12 Jan 2015 22:48:19 +0200 Subject: [PATCH 593/638] Added missing variable --- src/Composer/EventDispatcher/EventDispatcher.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index c5033124e..dff5456e1 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -44,6 +44,7 @@ class EventDispatcher protected $io; protected $loader; protected $process; + protected $listeners; /** * Constructor. From 395903863bf0659d074c900f0b162e21bd4e5212 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Thu, 15 Jan 2015 22:33:31 +0100 Subject: [PATCH 594/638] Add 'info' alias for 'show' command --- src/Composer/Command/ShowCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 28ea81028..907e99f90 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -41,6 +41,7 @@ class ShowCommand extends Command { $this ->setName('show') + ->setAliases(array('info')) ->setDescription('Show information about packages') ->setDefinition(array( new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect'), From 9d84a9216350a39cfb7ce0ebc5809b6116cf0f60 Mon Sep 17 00:00:00 2001 From: SofHad Date: Fri, 16 Jan 2015 20:59:30 +0100 Subject: [PATCH 595/638] Fixed json syntax error --- doc/05-repositories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 975b473e5..80d15c561 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -122,7 +122,7 @@ JSON request body: ```json { "downloads": [ - {"name": "monolog/monolog", "version": "1.2.1.0"}, + {"name": "monolog/monolog", "version": "1.2.1.0"} ] } ``` From 296252330e5e449792058fc7240846d1a99c9366 Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Mon, 19 Jan 2015 17:28:27 +0100 Subject: [PATCH 596/638] fix-token-retrying-itself --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index e03cf8ec8..455bda92e 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -343,7 +343,7 @@ class RemoteFilesystem { if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { $message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); - $gitHubUtil = new GitHub($this->io, $this->config, null, $this); + $gitHubUtil = new GitHub($this->io, $this->config, null); if (!$gitHubUtil->authorizeOAuth($this->originUrl) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message)) ) { From 2a1a963b0064ad57172d4aa0fb649f964edf6e33 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 19 Jan 2015 17:56:20 +0000 Subject: [PATCH 597/638] support bitbucket URLs with plain http --- src/Composer/Repository/Vcs/GitBitbucketDriver.php | 4 ++-- src/Composer/Repository/Vcs/HgBitbucketDriver.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 88a6d5454..68389dc33 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -33,7 +33,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface */ public function initialize() { - preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match); + preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match); $this->owner = $match[1]; $this->repository = $match[2]; $this->originUrl = 'bitbucket.org'; @@ -143,7 +143,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) { + if (!preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) { return false; } diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 5e6f71241..cc2b386eb 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -33,7 +33,7 @@ class HgBitbucketDriver extends VcsDriver */ public function initialize() { - preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match); + preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match); $this->owner = $match[1]; $this->repository = $match[2]; $this->originUrl = 'bitbucket.org'; @@ -153,7 +153,7 @@ class HgBitbucketDriver extends VcsDriver */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) { + if (!preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) { return false; } From 7bb85ff21e5f7989512190ba1f802733244be535 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Mon, 1 Dec 2014 18:09:35 +1300 Subject: [PATCH 598/638] Support aliases for numeric branches. Fixes #3461 --- doc/articles/aliases.md | 12 ++++++---- src/Composer/Package/Loader/ArrayLoader.php | 10 ++++++++- .../Package/Loader/ValidatingArrayLoader.php | 11 ++++++++++ .../Package/Version/VersionParser.php | 15 +++++++++++++ .../Test/Package/Loader/ArrayLoaderTest.php | 22 +++++++++++++++++++ .../Loader/ValidatingArrayLoaderTest.php | 15 +++++++++++++ .../Package/Version/VersionParserTest.php | 20 +++++++++++++++++ .../Package/Version/VersionSelectorTest.php | 2 ++ 8 files changed, 102 insertions(+), 5 deletions(-) diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index 2b436322f..79c573d3d 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -38,10 +38,14 @@ specifying a `branch-alias` field under `extra` in `composer.json`: } ``` -The branch version must begin with `dev-` (non-comparable version), the alias -must be a comparable dev version (i.e. start with numbers, and end with -`.x-dev`). The `branch-alias` must be present on the branch that it references. -For `dev-master`, you need to commit it on the `master` branch. +If you alias a non-comparible version (such as dev-develop) `dev-` must prefix the +branch name. You may also alias a comparible version (i.e. start with numbers, +and end with `.x-dev`), but only as a more specific version. +For example, 1.x-dev could be aliased as 1.2.x-dev. + +The alias must be a comparable dev version, and the `branch-alias` must be present on +the branch that it references. For `dev-master`, you need to commit it on the +`master` branch. As a result, anyone can now require `1.0.*` and it will happily install `dev-master`. diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 243b7b574..558a24e35 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -224,7 +224,7 @@ class ArrayLoader implements LoaderInterface */ public function getBranchAlias(array $config) { - if ('dev-' !== substr($config['version'], 0, 4) + if (('dev-' !== substr($config['version'], 0, 4) && '-dev' !== substr($config['version'], -4)) || !isset($config['extra']['branch-alias']) || !is_array($config['extra']['branch-alias']) ) { @@ -248,6 +248,14 @@ class ArrayLoader implements LoaderInterface continue; } + // If using numeric aliases ensure the alias is a valid subversion + if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) + && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) + && (stripos($targetPrefix, $sourcePrefix) !== 0) + ) { + continue; + } + return $validatedTargetBranch; } } diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 3493d3d5b..9a6f4dd32 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -251,6 +251,17 @@ class ValidatingArrayLoader implements LoaderInterface if ('-dev' !== substr($validatedTargetBranch, -4)) { $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must be a parseable number like 2.0-dev'; unset($this->config['extra']['branch-alias'][$sourceBranch]); + + continue; + } + + // If using numeric aliases ensure the alias is a valid subversion + if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) + && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) + && (stripos($targetPrefix, $sourcePrefix) !== 0) + ) { + $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') is not a valid numeric alias for this version'; + unset($this->config['extra']['branch-alias'][$sourceBranch]); } } } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 2a2c38371..3925e7e65 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -169,6 +169,21 @@ class VersionParser throw new \UnexpectedValueException('Invalid version string "'.$version.'"'.$extraMessage); } + /** + * Extract numeric prefix from alias, if it is in numeric format, suitable for + * version comparison + * + * @param string $branch Branch name (e.g. 2.1.x-dev) + * @return string|false Numeric prefix if present (e.g. 2.1.) or false + */ + public function parseNumericAliasPrefix($branch) { + if(preg_match('/^(?(\d+\\.)*\d+).x-dev$/i', $branch, $matches)) { + return $matches['version']."."; + } else { + return false; + } + } + /** * Normalizes a branch name to be able to perform comparisons on it * diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 6e4e2f5ee..4b98ae26d 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -138,6 +138,28 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); + + $config = array( + 'name' => 'B', + 'version' => '4.x-dev', + 'extra' => array('branch-alias' => array('4.x-dev' => '4.0.x-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\AliasPackage', $package); + $this->assertEquals('4.0.x-dev', $package->getPrettyVersion()); + + $config = array( + 'name' => 'C', + 'version' => '4.x-dev', + 'extra' => array('branch-alias' => array('4.x-dev' => '3.4.x-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\CompletePackage', $package); + $this->assertEquals('4.x-dev', $package->getPrettyVersion()); } public function testAbandoned() diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 23c47c3c7..2b0829759 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -140,6 +140,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'branch-alias' => array( 'dev-master' => '2.0-dev', 'dev-old' => '1.0.x-dev', + '3.x-dev' => '3.1.x-dev' ), ), 'bin' => array( @@ -324,6 +325,20 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase ), false ), + array( + array( + 'name' => 'foo/bar', + 'extra' => array( + 'branch-alias' => array( + '5.x-dev' => '3.1.x-dev' + ), + ) + ), + array( + 'extra.branch-alias.5.x-dev : the target branch (3.1.x-dev) is not a valid numeric alias for this version' + ), + false + ), ); } } diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 88058f6aa..c52ec1382 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -76,6 +76,26 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame($expected, $parser->normalize($input)); } + public function numericAliasVersions() { + return array( + array('0.x-dev', '0.'), + array('1.0.x-dev', '1.0.'), + array('1.x-dev', '1.'), + array('1.2.x-dev', '1.2.'), + array('dev-develop', false), + array('dev-master', false), + ); + } + + /** + * @dataProvider numericAliasVersions + */ + public function testParseNumericAliasPrefix($input, $expected) + { + $parser = new VersionParser; + $this->assertSame($expected, $parser->parseNumericAliasPrefix($input)); + } + public function successfulNormalizedVersions() { return array( diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 90f820e8f..fb3a6f666 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -115,6 +115,8 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase array('dev-master', true, 'dev', '~2.1@dev', '2.1.x-dev'), array('dev-master', true, 'dev', '~2.1@dev', '2.1.3.x-dev'), array('dev-master', true, 'dev', '~2.0@dev', '2.x-dev'), + // numeric alias + array('3.x-dev', true, 'dev', '~3.0@dev', '3.0.x-dev'), ); } From ea3736522d4f99e93e349d6e7491a443a6aac8d3 Mon Sep 17 00:00:00 2001 From: Robin Hallabro Date: Tue, 20 Jan 2015 09:05:40 +0100 Subject: [PATCH 599/638] Update documententation for version shorthand parsing --- doc/02-libraries.md | 4 ++-- doc/04-schema.md | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 561f3aa79..913f996b7 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -77,8 +77,8 @@ you can just add a `version` field: For every tag that looks like a version, a package version of that tag will be created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix -of `-patch`, `-alpha`, `-beta` or `-RC`. The suffixes can also be followed by -a number. +of `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffixes +can also be followed by a number. Here are a few examples of valid tag names: diff --git a/doc/04-schema.md b/doc/04-schema.md index 8662ca855..3acfef735 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -54,8 +54,8 @@ The version of the package. In most cases this is not required and should be omitted (see below). This must follow the format of `X.Y.Z` or `vX.Y.Z` with an optional suffix -of `-dev`, `-patch`, `-alpha`, `-beta` or `-RC`. The patch, alpha, beta and -RC suffixes can also be followed by a number. +of `-dev`, `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. +The patch, alpha, beta and RC suffixes can also be followed by a number. Examples: @@ -67,6 +67,7 @@ Examples: - 1.0.0-alpha3 - 1.0.0-beta2 - 1.0.0-RC5 +- v2.0.4-p1 Optional if the package repository can infer the version from somewhere, such as the VCS tag name in the VCS repository. In that case it is also recommended From af161e242e0990f0c64ad2acc44ae0db38f522e9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 20 Jan 2015 09:55:18 +0000 Subject: [PATCH 600/638] Add more troubleshooting info, refs #3457 --- doc/articles/troubleshooting.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index 922526a52..4de3ad19e 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -114,8 +114,9 @@ php -d memory_limit=-1 composer.phar <...> ## "The system cannot find the path specified" (Windows) 1. Open regedit. -2. Search for an ```AutoRun``` key inside ```HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor``` - or ```HKEY_CURRENT_USER\Software\Microsoft\Command Processor```. +2. Search for an `AutoRun` key inside `HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor`, + `HKEY_CURRENT_USER\Software\Microsoft\Command Processor` + or `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Command Processor`. 3. Check if it contains any path to non-existent file, if it's the case, just remove them. ## API rate limit and OAuth tokens From 3b678ee37939e4a5cd0a7a1eddba5c11d282a879 Mon Sep 17 00:00:00 2001 From: Dmitry Tarasov Date: Tue, 20 Jan 2015 13:03:55 +0300 Subject: [PATCH 601/638] Fix error handling for incorrect "repositories" array --- src/Composer/Config.php | 2 +- tests/Composer/Test/ConfigTest.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 86e318765..a144467aa 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -124,7 +124,7 @@ class Config } // disable a repository with an anonymous {"name": false} repo - if (1 === count($repository) && false === current($repository)) { + if (is_array($repository) && 1 === count($repository) && false === current($repository)) { unset($this->repositories[key($repository)]); continue; } diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 99970125c..e50af5e87 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -97,6 +97,18 @@ class ConfigTest extends \PHPUnit_Framework_TestCase ), ); + $data['incorrect local config does not cause ErrorException'] = array( + array( + 'packagist' => array('type' => 'composer', 'url' => 'https?://packagist.org', 'allow_ssl_downgrade' => true), + 'type' => 'vcs', + 'url' => 'http://example.com', + ), + array( + 'type' => 'vcs', + 'url' => 'http://example.com', + ), + ); + return $data; } From cd38d1e096b403229363fce98fd671c71f644649 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 20 Jan 2015 10:26:10 +0000 Subject: [PATCH 602/638] Tweaks to also support 1.0-dev style aliases, refs #3480 --- .../Package/Version/VersionParser.php | 9 ++-- .../Test/Package/Loader/ArrayLoaderTest.php | 22 ++++++++++ .../Loader/ValidatingArrayLoaderTest.php | 14 ++++++ .../Package/Version/VersionParserTest.php | 43 ++++++++++--------- .../Package/Version/VersionSelectorTest.php | 2 + 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 3925e7e65..d546b2655 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -176,12 +176,13 @@ class VersionParser * @param string $branch Branch name (e.g. 2.1.x-dev) * @return string|false Numeric prefix if present (e.g. 2.1.) or false */ - public function parseNumericAliasPrefix($branch) { - if(preg_match('/^(?(\d+\\.)*\d+).x-dev$/i', $branch, $matches)) { + public function parseNumericAliasPrefix($branch) + { + if (preg_match('/^(?(\d+\\.)*\d+)(?:\.x)?-dev$/i', $branch, $matches)) { return $matches['version']."."; - } else { - return false; } + + return false; } /** diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 4b98ae26d..1491571a1 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -139,6 +139,17 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); + $config = array( + 'name' => 'A', + 'version' => 'dev-master', + 'extra' => array('branch-alias' => array('dev-master' => '1.0-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\AliasPackage', $package); + $this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); + $config = array( 'name' => 'B', 'version' => '4.x-dev', @@ -150,6 +161,17 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertEquals('4.0.x-dev', $package->getPrettyVersion()); + $config = array( + 'name' => 'B', + 'version' => '4.x-dev', + 'extra' => array('branch-alias' => array('4.x-dev' => '4.0-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\AliasPackage', $package); + $this->assertEquals('4.0.x-dev', $package->getPrettyVersion()); + $config = array( 'name' => 'C', 'version' => '4.x-dev', diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 2b0829759..32c55bd40 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -339,6 +339,20 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase ), false ), + array( + array( + 'name' => 'foo/bar', + 'extra' => array( + 'branch-alias' => array( + '5.x-dev' => '3.1-dev' + ), + ) + ), + array( + 'extra.branch-alias.5.x-dev : the target branch (3.1-dev) is not a valid numeric alias for this version' + ), + false + ), ); } } diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index c52ec1382..13920e2f0 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -67,26 +67,6 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase return array_map($createPackage, $data); } - /** - * @dataProvider successfulNormalizedVersions - */ - public function testNormalizeSucceeds($input, $expected) - { - $parser = new VersionParser; - $this->assertSame($expected, $parser->normalize($input)); - } - - public function numericAliasVersions() { - return array( - array('0.x-dev', '0.'), - array('1.0.x-dev', '1.0.'), - array('1.x-dev', '1.'), - array('1.2.x-dev', '1.2.'), - array('dev-develop', false), - array('dev-master', false), - ); - } - /** * @dataProvider numericAliasVersions */ @@ -96,6 +76,29 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame($expected, $parser->parseNumericAliasPrefix($input)); } + public function numericAliasVersions() + { + return array( + array('0.x-dev', '0.'), + array('1.0.x-dev', '1.0.'), + array('1.x-dev', '1.'), + array('1.2.x-dev', '1.2.'), + array('1.2-dev', '1.2.'), + array('1-dev', '1.'), + array('dev-develop', false), + array('dev-master', false), + ); + } + + /** + * @dataProvider successfulNormalizedVersions + */ + public function testNormalizeSucceeds($input, $expected) + { + $parser = new VersionParser; + $this->assertSame($expected, $parser->normalize($input)); + } + public function successfulNormalizedVersions() { return array( diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index fb3a6f666..6feb207a7 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -113,10 +113,12 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase array('3.1.2-dev', true, 'dev', '3.1.2-dev'), // dev packages with alias inherit the alias array('dev-master', true, 'dev', '~2.1@dev', '2.1.x-dev'), + array('dev-master', true, 'dev', '~2.1@dev', '2.1-dev'), array('dev-master', true, 'dev', '~2.1@dev', '2.1.3.x-dev'), array('dev-master', true, 'dev', '~2.0@dev', '2.x-dev'), // numeric alias array('3.x-dev', true, 'dev', '~3.0@dev', '3.0.x-dev'), + array('3.x-dev', true, 'dev', '~3.0@dev', '3.0-dev'), ); } From f50cfe6d4539b7521467fc7fca7b46d29299ab2c Mon Sep 17 00:00:00 2001 From: Dmitry Tarasov Date: Tue, 20 Jan 2015 13:44:29 +0300 Subject: [PATCH 603/638] Improve error message for case with incorrect "repositories" --- src/Composer/Factory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index f2aa27e22..fdb5982e4 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -167,10 +167,10 @@ class Factory foreach ($config->getRepositories() as $index => $repo) { if (!is_array($repo)) { - throw new \UnexpectedValueException('Repository '.$index.' ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); + throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); } if (!isset($repo['type'])) { - throw new \UnexpectedValueException('Repository '.$index.' ('.json_encode($repo).') must have a type defined'); + throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') must have a type defined'); } $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; while (isset($repos[$name])) { From c8011af219bbfe832d3dc7eeba447e12fe0df4c7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 20 Jan 2015 10:49:03 +0000 Subject: [PATCH 604/638] Improve message some more, refs #3655 --- src/Composer/Factory.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index fdb5982e4..a6597afd7 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -166,6 +166,9 @@ class Factory } foreach ($config->getRepositories() as $index => $repo) { + if (is_string($repo)) { + throw new \UnexpectedValueException('Repositories should be an array of repository defintions, only a single repository was given'); + } if (!is_array($repo)) { throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); } From 825b4b9c63a29c586d005de8dbbcfdfeb86bbf6f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 20 Jan 2015 16:39:06 +0000 Subject: [PATCH 605/638] Improve error message and fix typo, closes #3657 --- src/Composer/Factory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index a6597afd7..3f0041a85 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -167,7 +167,7 @@ class Factory foreach ($config->getRepositories() as $index => $repo) { if (is_string($repo)) { - throw new \UnexpectedValueException('Repositories should be an array of repository defintions, only a single repository was given'); + throw new \UnexpectedValueException('"repositories" should be an array of repository definitions, only a single repository was given'); } if (!is_array($repo)) { throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); From 5b8f9b0390d6d111a1f3fca957104c8b23157d0d Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Wed, 21 Jan 2015 15:40:43 +0100 Subject: [PATCH 606/638] Altered output of diagnose - added some docblocks - added rate limit check for users that have no oauth token configured - made it 'scream' less (yellow text instead of yellow background) - reformatted the php ini checks slightly to be more condensed --- src/Composer/Command/DiagnoseCommand.php | 101 +++++++++++++++++------ 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 92ccf9291..3ef83808f 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -29,8 +29,13 @@ use Symfony\Component\Console\Output\OutputInterface; */ class DiagnoseCommand extends Command { + /** @var RemoteFileSystem */ protected $rfs; + + /** @var ProcessExecutor */ protected $process; + + /** @var int */ protected $failures = 0; protected function configure() @@ -49,6 +54,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $composer = $this->getComposer(false); + if ($composer) { $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); @@ -90,6 +96,24 @@ EOT $output->write('Checking '.$domain.' oauth access: '); $this->outputResult($output, $this->checkGithubOauth($domain, $token)); } + } else { + $output->write('Checking github.com rate limit: '); + $rate = $this->getGithubRateLimit('github.com'); + + if (10 > $rate['remaining']) { + $output->writeln('WARNING'); + $output->writeln(sprintf( + 'Github has a rate limit on their API. ' + . 'You currently have %u ' + . 'out of %u requests left.' . PHP_EOL + . 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL + . ' https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens', + $rate['remaining'], + $rate['limit'] + )); + } else { + $output->writeln('OK'); + } } $output->write('Checking disk free space: '); @@ -129,7 +153,7 @@ EOT { $this->process->execute('git config color.ui', $output); if (strtolower(trim($output)) === 'always') { - return 'Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.'; + return 'Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.'; } return true; @@ -139,7 +163,7 @@ EOT { $protocol = extension_loaded('openssl') ? 'https' : 'http'; try { - $json = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false); + $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false); } catch (\Exception $e) { return $e; } @@ -207,7 +231,7 @@ EOT $url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0'; try { - $rfcResult = $this->rfs->getContents('github.com', $url, false); + $this->rfs->getContents('github.com', $url, false); } catch (TransportException $e) { try { $this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false))); @@ -227,10 +251,33 @@ EOT try { $url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos'; - return $this->rfs->getContents($domain, $url, false) ? true : 'Unexpected error'; + return $this->rfs->getContents($domain, $url, false, array( + 'retry-auth-failure' => false + )) ? true : 'Unexpected error'; } catch (\Exception $e) { if ($e instanceof TransportException && $e->getCode() === 401) { - return 'The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it'; + return 'The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it'; + } + + return $e; + } + } + + private function getGithubRateLimit($domain, $token = null) + { + if ($token) { + $this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic'); + } + + try { + $url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit'; + $json = $this->rfs->getContents($domain, $url, array('retry-auth-failure' => false)); + $data = json_decode($json, true); + + return $data['rate']; + } catch (\Exception $e) { + if ($e instanceof TransportException && $e->getCode() === 401) { + return 'The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it'; } return $e; @@ -255,7 +302,7 @@ EOT $latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false)); if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') { - return 'You are not running the latest version'; + return 'You are not running the latest version'; } return true; @@ -271,7 +318,7 @@ EOT if ($result instanceof \Exception) { $output->writeln('['.get_class($result).'] '.$result->getMessage()); } elseif ($result) { - $output->writeln($result); + $output->writeln(trim($result)); } } } @@ -280,7 +327,7 @@ EOT { $output = ''; $out = function ($msg, $style) use (&$output) { - $output .= '<'.$style.'>'.$msg.''; + $output .= '<'.$style.'>'.$msg.''.PHP_EOL; }; // code below taken from getcomposer.org/installer, any changes should be made there and replicated here @@ -341,13 +388,13 @@ EOT foreach ($errors as $error => $current) { switch ($error) { case 'php': - $text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher."; + $text = "- Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher."; break; case 'allow_url_fopen': - $text = PHP_EOL."The allow_url_fopen setting is incorrect.".PHP_EOL; + $text = "The allow_url_fopen setting is incorrect.".PHP_EOL; $text .= "Add the following to the end of your `php.ini`:".PHP_EOL; - $text .= " allow_url_fopen = On"; + $text .= " allow_url_fopen = On"; $displayIniMessage = true; break; } @@ -361,51 +408,51 @@ EOT foreach ($warnings as $warning => $current) { switch ($warning) { case 'apc_cli': - $text = PHP_EOL."The apc.enable_cli setting is incorrect.".PHP_EOL; + $text = "The apc.enable_cli setting is incorrect.".PHP_EOL; $text .= "Add the following to the end of your `php.ini`:".PHP_EOL; - $text .= " apc.enable_cli = Off"; + $text .= " apc.enable_cli = Off"; $displayIniMessage = true; break; case 'sigchild': - $text = PHP_EOL."PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL; + $text = "PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL; $text .= "Recompile it without this flag if possible, see also:".PHP_EOL; - $text .= " https://bugs.php.net/bug.php?id=22999"; + $text .= " https://bugs.php.net/bug.php?id=22999"; break; case 'curlwrappers': - $text = PHP_EOL."PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL; - $text .= "Recompile it without this flag if possible"; + $text = "PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL; + $text .= " Recompile it without this flag if possible"; break; case 'openssl': - $text = PHP_EOL."The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL; - $text .= "If possible you should enable it or recompile php with --with-openssl"; + $text = "The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL; + $text .= " If possible you should enable it or recompile php with --with-openssl"; break; case 'php': - $text = PHP_EOL."Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL; - $text .= "Composer works with 5.3.2+ for most people, but there might be edge case issues."; + $text = "Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL; + $text .= " Composer works with 5.3.2+ for most people, but there might be edge case issues."; break; case 'xdebug_loaded': - $text = PHP_EOL."The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL; - $text .= "Disabling it when using Composer is recommended, but should not cause issues beyond slowness."; + $text = "The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL; + $text .= " Disabling it when using Composer is recommended."; break; case 'xdebug_profile': - $text = PHP_EOL."The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL; + $text = "The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL; $text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL; - $text .= " xdebug.profiler_enabled = 0"; + $text .= " xdebug.profiler_enabled = 0"; $displayIniMessage = true; break; } - $out($text, 'warning'); + $out($text, 'comment'); } } if ($displayIniMessage) { - $out($iniMessage, 'warning'); + $out($iniMessage, 'comment'); } return !$warnings && !$errors ? true : $output; From 4038477a7f3d1d349c4dbdce6d2f79090fc4549d Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Wed, 21 Jan 2015 15:44:59 +0100 Subject: [PATCH 607/638] Missed a - --- src/Composer/Command/DiagnoseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 3ef83808f..78cf85db9 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -388,7 +388,7 @@ EOT foreach ($errors as $error => $current) { switch ($error) { case 'php': - $text = "- Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher."; + $text = "Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher."; break; case 'allow_url_fopen': From 506d923e7c1e7a6d06467a35b8af3804ca5767d3 Mon Sep 17 00:00:00 2001 From: Dmitry Tarasov Date: Thu, 22 Jan 2015 11:26:25 +0300 Subject: [PATCH 608/638] #3663 fix error message for config command --- src/Composer/Command/ConfigCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 4d52c8473..9d27464b5 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -137,7 +137,7 @@ EOT } if (!$this->configFile->exists()) { - throw new \RuntimeException('No composer.json found in the current directory'); + throw new \RuntimeException("File '$configFile' cannot be found in the current directory"); } } From cdcf39b2f0ca0a66d03d8702d647effa52beb32a Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Thu, 22 Jan 2015 09:45:26 +0100 Subject: [PATCH 609/638] Don't show download progress --- src/Composer/Command/DiagnoseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 78cf85db9..0d24d74e6 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -271,7 +271,7 @@ EOT try { $url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit'; - $json = $this->rfs->getContents($domain, $url, array('retry-auth-failure' => false)); + $json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false)); $data = json_decode($json, true); return $data['rate']; From e047ca994448d319952a1639b86e4d521f724dfd Mon Sep 17 00:00:00 2001 From: Dmitry Tarasov Date: Fri, 23 Jan 2015 10:52:53 +0300 Subject: [PATCH 610/638] #3665 fix exception message coding style --- src/Composer/Command/ConfigCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 9d27464b5..c65afdb7d 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -137,7 +137,7 @@ EOT } if (!$this->configFile->exists()) { - throw new \RuntimeException("File '$configFile' cannot be found in the current directory"); + throw new \RuntimeException(sprintf('File "%s" cannot be found in the current directory', $configFile)); } } From 59141c8663b80c0baeed40c268af07ff54fd6d9c Mon Sep 17 00:00:00 2001 From: Josh Pollock Date: Sun, 25 Jan 2015 17:05:21 -0500 Subject: [PATCH 611/638] Add note about /usr not existing in OSX Yosemite by default. BTW Seriously Apple? --- doc/00-intro.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/00-intro.md b/doc/00-intro.md index c1bafa3cd..a927f35fb 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -107,6 +107,8 @@ mv composer.phar /usr/local/bin/composer > **Note:** If the above fails due to permissions, run the `mv` line > again with sudo. +> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you recive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/composer/` manually. + Then, just run `composer` in order to run Composer instead of `php composer.phar`. ## Installation - Windows From 5e5eb069dceef1c584a4c9fa570b0dfe61abcf55 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 27 Jan 2015 16:56:39 +0100 Subject: [PATCH 612/638] Don't use a regex to parse installer tests to allow for longer tests --- tests/Composer/Test/InstallerTest.php | 143 ++++++++++++++++---------- 1 file changed, 90 insertions(+), 53 deletions(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 9bf2c01a7..2c77f9bc3 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -241,10 +241,10 @@ class InstallerTest extends TestCase } $installationManager = $composer->getInstallationManager(); - $this->assertSame($expect, implode("\n", $installationManager->getTrace())); + $this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace())); if ($expectOutput) { - $this->assertEquals($expectOutput, $output); + $this->assertEquals(rtrim($expectOutput), rtrim($output)); } } @@ -258,21 +258,7 @@ class InstallerTest extends TestCase continue; } - $test = file_get_contents($file->getRealpath()); - - $content = '(?:.(?!--[A-Z]))+'; - $pattern = '{^ - --TEST--\s*(?P.*?)\s* - (?:--CONDITION--\s*(?P'.$content.'))?\s* - --COMPOSER--\s*(?P'.$content.')\s* - (?:--LOCK--\s*(?P'.$content.'))?\s* - (?:--INSTALLED--\s*(?P'.$content.'))?\s* - --RUN--\s*(?P.*?)\s* - (?:--EXPECT-LOCK--\s*(?P'.$content.'))?\s* - (?:--EXPECT-OUTPUT--\s*(?P'.$content.'))?\s* - (?:--EXPECT-EXIT-CODE--\s*(?P\d+))?\s* - --EXPECT--\s*(?P.*?)\s* - $}xs'; + $testData = $this->readTestFile($file, $fixturesDir); $installed = array(); $installedDev = array(); @@ -280,48 +266,44 @@ class InstallerTest extends TestCase $expectLock = array(); $expectExitCode = 0; - if (preg_match($pattern, $test, $match)) { - try { - $message = $match['test']; - $condition = !empty($match['condition']) ? $match['condition'] : null; - $composer = JsonFile::parseJson($match['composer']); + try { + $message = $testData['TEST']; + $condition = !empty($testData['CONDITION']) ? $testData['CONDITION'] : null; + $composer = JsonFile::parseJson($testData['COMPOSER']); - if (isset($composer['repositories'])) { - foreach ($composer['repositories'] as &$repo) { - if ($repo['type'] !== 'composer') { - continue; - } - - // Change paths like file://foobar to file:///path/to/fixtures - if (preg_match('{^file://[^/]}', $repo['url'])) { - $repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7); - } - - unset($repo); + if (isset($composer['repositories'])) { + foreach ($composer['repositories'] as &$repo) { + if ($repo['type'] !== 'composer') { + continue; } - } - if (!empty($match['lock'])) { - $lock = JsonFile::parseJson($match['lock']); - if (!isset($lock['hash'])) { - $lock['hash'] = md5(json_encode($composer)); + // Change paths like file://foobar to file:///path/to/fixtures + if (preg_match('{^file://[^/]}', $repo['url'])) { + $repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7); } + + unset($repo); } - if (!empty($match['installed'])) { - $installed = JsonFile::parseJson($match['installed']); - } - $run = $match['run']; - if (!empty($match['expectLock'])) { - $expectLock = JsonFile::parseJson($match['expectLock']); - } - $expectOutput = $match['expectOutput']; - $expect = $match['expect']; - $expectExitCode = (int) $match['expectExitCode']; - } catch (\Exception $e) { - die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); } - } else { - die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); + + if (!empty($testData['LOCK'])) { + $lock = JsonFile::parseJson($testData['LOCK']); + if (!isset($lock['hash'])) { + $lock['hash'] = md5(json_encode($composer)); + } + } + if (!empty($testData['INSTALLED'])) { + $installed = JsonFile::parseJson($testData['INSTALLED']); + } + $run = $testData['RUN']; + if (!empty($testData['EXPECT-LOCK'])) { + $expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']); + } + $expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null; + $expect = $testData['EXPECT']; + $expectExitCode = isset($testData['EXPECT-EXIT-CODE']) ? (int) $testData['EXPECT-EXIT-CODE'] : 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); @@ -329,4 +311,59 @@ class InstallerTest extends TestCase return $tests; } + + protected function readTestFile(\SplFileInfo $file, $fixturesDir) + { + $tokens = preg_split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file->getRealPath()), null, PREG_SPLIT_DELIM_CAPTURE); + + $sectionInfo = array( + 'TEST' => true, + 'CONDITION' => false, + 'COMPOSER' => true, + 'LOCK' => false, + 'INSTALLED' => false, + 'RUN' => true, + 'EXPECT-LOCK' => false, + 'EXPECT-OUTPUT' => false, + 'EXPECT-EXIT-CODE' => false, + 'EXPECT' => true, + ); + + $section = null; + foreach ($tokens as $i => $token) + { + if (null === $section && empty($token)) { + continue; // skip leading blank + } + + if (null === $section) { + if (!isset($sectionInfo[$token])) { + throw new \RuntimeException(sprintf( + 'The test file "%s" must not contain a section named "%s".', + str_replace($fixturesDir.'/', '', $file), + $token + )); + } + $section = $token; + continue; + } + + $sectionData = $token; + + $data[$section] = $sectionData; + $section = $sectionData = null; + } + + foreach ($sectionInfo as $section => $required) { + if ($required && !isset($data[$section])) { + throw new \RuntimeException(sprintf( + 'The test file "%s" must have a section named "%s".', + str_replace($fixturesDir.'/', '', $file), + $section + )); + } + } + + return $data; + } } From 82831bec130346f6c00446e19ce15a7c24003606 Mon Sep 17 00:00:00 2001 From: findstar Date: Wed, 28 Jan 2015 15:07:44 +0900 Subject: [PATCH 613/638] I think 'dump-autoload' command is more proper. --- doc/01-basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index d83e79aed..ef72f556c 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -213,7 +213,7 @@ You define a mapping from namespaces to directories. The `src` directory would be in your project root, on the same level as `vendor` directory is. An example filename would be `src/Foo.php` containing an `Acme\Foo` class. -After adding the `autoload` field, you have to re-run `install` to re-generate +After adding the `autoload` field, you have to re-run `dump-autoload` to re-generate the `vendor/autoload.php` file. Including that file will also return the autoloader instance, so you can store From bb0a2df293807b04410477abefcd92731e7f04d0 Mon Sep 17 00:00:00 2001 From: Xavier Fornes Date: Wed, 28 Jan 2015 08:49:38 +0100 Subject: [PATCH 614/638] fix for issue #3657 ConsoleIO writing a message which contains end of lines characters --- src/Composer/IO/ConsoleIO.php | 14 ++++++++------ tests/Composer/Test/IO/ConsoleIOTest.php | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 491a6b7fb..78b271356 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -97,12 +97,14 @@ class ConsoleIO extends BaseIO { if (null !== $this->startTime) { $messages = (array) $messages; - $messages[0] = sprintf( - '[%.1fMB/%.2fs] %s', - memory_get_usage() / 1024 / 1024, - microtime(true) - $this->startTime, - $messages[0] - ); + $messages = array_map(function (&$message) { + return sprintf( + '[%.1fMB/%.2fs] %s', + memory_get_usage() / 1024 / 1024, + microtime(true) - $this->startTime, + $message + ); + }, $messages); } $this->output->write($messages, $newline); $this->lastMessage = join($newline ? "\n" : '', (array) $messages); diff --git a/tests/Composer/Test/IO/ConsoleIOTest.php b/tests/Composer/Test/IO/ConsoleIOTest.php index 6ce95b0ac..8c9fad5fa 100644 --- a/tests/Composer/Test/IO/ConsoleIOTest.php +++ b/tests/Composer/Test/IO/ConsoleIOTest.php @@ -49,6 +49,30 @@ class ConsoleIOTest extends TestCase $consoleIO->write('some information about something', false); } + public function testWriteWithMultipleLineStringWhenDebugging() + { + $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $outputMock->expects($this->once()) + ->method('write') + ->with( + $this->callback(function($messages){ + $this->assertRegExp("[(.*)/(.*) First line]", $messages[0]); + $this->assertRegExp("[(.*)/(.*) Second line]", $messages[1]); + return true; + }), + $this->equalTo(false) + ); + $helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet'); + + $consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock); + $startTime = microtime(true); + $consoleIO->enableDebugging($startTime); + + $example = explode('\n', 'First line\nSecond lines'); + $consoleIO->write($example, false); + } + public function testOverwrite() { $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); From 8ba8580a28a4b122ee002bd31e8b87df79f32960 Mon Sep 17 00:00:00 2001 From: Xavier Fornes Date: Wed, 28 Jan 2015 11:46:38 +0100 Subject: [PATCH 615/638] fix test implementation for php 5.3 refs issue #3657 --- tests/Composer/Test/IO/ConsoleIOTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/IO/ConsoleIOTest.php b/tests/Composer/Test/IO/ConsoleIOTest.php index 8c9fad5fa..c83ec6296 100644 --- a/tests/Composer/Test/IO/ConsoleIOTest.php +++ b/tests/Composer/Test/IO/ConsoleIOTest.php @@ -57,9 +57,9 @@ class ConsoleIOTest extends TestCase ->method('write') ->with( $this->callback(function($messages){ - $this->assertRegExp("[(.*)/(.*) First line]", $messages[0]); - $this->assertRegExp("[(.*)/(.*) Second line]", $messages[1]); - return true; + $result = preg_match("[(.*)/(.*) First line]", $messages[0]) > 0; + $result &= preg_match("[(.*)/(.*) Second line]", $messages[1]) > 0; + return $result; }), $this->equalTo(false) ); From 3f59c007f776fde89a1c11e2776a9fde3a8ae378 Mon Sep 17 00:00:00 2001 From: Xavier Fornes Date: Wed, 28 Jan 2015 11:50:54 +0100 Subject: [PATCH 616/638] fix implementation for php 5.3 refs issue #3657 --- src/Composer/IO/ConsoleIO.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 78b271356..11623a143 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -97,11 +97,12 @@ class ConsoleIO extends BaseIO { if (null !== $this->startTime) { $messages = (array) $messages; - $messages = array_map(function (&$message) { + $startTime = $this->startTime; + $messages = array_map(function (&$message) use ($startTime) { return sprintf( '[%.1fMB/%.2fs] %s', memory_get_usage() / 1024 / 1024, - microtime(true) - $this->startTime, + microtime(true) - $startTime, $message ); }, $messages); From a4ffda699f2bcb57d0a0b2aa7bc9fd1d68888412 Mon Sep 17 00:00:00 2001 From: Xavier Fornes Date: Wed, 28 Jan 2015 13:38:43 +0100 Subject: [PATCH 617/638] remove by reference on message parameter refs issue #3657 --- src/Composer/IO/ConsoleIO.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 11623a143..28a2247cd 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -98,7 +98,7 @@ class ConsoleIO extends BaseIO if (null !== $this->startTime) { $messages = (array) $messages; $startTime = $this->startTime; - $messages = array_map(function (&$message) use ($startTime) { + $messages = array_map(function ($message) use ($startTime) { return sprintf( '[%.1fMB/%.2fs] %s', memory_get_usage() / 1024 / 1024, From 2936dc0e55f823251aaad05092c05d28cfb86279 Mon Sep 17 00:00:00 2001 From: Xavier Fornes Date: Wed, 28 Jan 2015 14:43:58 +0100 Subject: [PATCH 618/638] some micro-optimizations made refs issue #3657 --- src/Composer/IO/ConsoleIO.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 28a2247cd..7df26eabf 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -96,16 +96,11 @@ class ConsoleIO extends BaseIO public function write($messages, $newline = true) { if (null !== $this->startTime) { - $messages = (array) $messages; - $startTime = $this->startTime; - $messages = array_map(function ($message) use ($startTime) { - return sprintf( - '[%.1fMB/%.2fs] %s', - memory_get_usage() / 1024 / 1024, - microtime(true) - $startTime, - $message - ); - }, $messages); + $memoryUsage = memory_get_usage() / 1024 / 1024; + $timeSpent = microtime(true) - $this->startTime; + $messages = array_map(function ($message) use ($memoryUsage, $timeSpent) { + return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message); + }, (array) $messages); } $this->output->write($messages, $newline); $this->lastMessage = join($newline ? "\n" : '', (array) $messages); From 32218e97d14432d657e23faac42571546482f1ff Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 8 Dec 2014 01:03:58 +0100 Subject: [PATCH 619/638] Added deprecated warning for the dev option --- src/Composer/Command/InstallCommand.php | 4 ++++ src/Composer/Command/UpdateCommand.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 669c03582..1b41bd7ec 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -74,6 +74,10 @@ EOT $input->setOption('no-plugins', true); } + if ($input->getOption('dev')) { + $output->writeln('You are using the deprecated option "dev". Dev packages are installed by default now.'); + } + $composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index ebfd4cb2a..c8e1f8587 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -76,6 +76,10 @@ EOT $input->setOption('no-plugins', true); } + if ($input->getOption('dev')) { + $output->writeln('You are using the deprecated option "dev". Dev packages are installed by default now.'); + } + $composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); From 87b7de4d0a5b9adcd5199a6b77c1fff46a5ff5e2 Mon Sep 17 00:00:00 2001 From: Chris Brand Date: Mon, 26 Jan 2015 17:07:58 +0200 Subject: [PATCH 620/638] Add the P character to the regex pattern According to http://php.net/manual/en/function.preg-match.php and some other sources named groups should contain a 'P' character after the '?' Without this, I receive the following error when running an update: [ErrorException] preg_match(): Compilation failed: unrecognized character after (?< at offset 4 Exception trace: () at phar:///var/www/git/smmqa/app/admin/composer.phar/src/Composer/Package/Version/VersionParser.php:181 --- src/Composer/Package/Version/VersionParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index d546b2655..9707ac017 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -178,7 +178,7 @@ class VersionParser */ public function parseNumericAliasPrefix($branch) { - if (preg_match('/^(?(\d+\\.)*\d+)(?:\.x)?-dev$/i', $branch, $matches)) { + if (preg_match('/^(?P(\d+\\.)*\d+)(?:\.x)?-dev$/i', $branch, $matches)) { return $matches['version']."."; } From e7f40140189935b7515ba48d6c1b16f30899b754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 28 Jan 2015 20:00:16 +0100 Subject: [PATCH 621/638] Reuse current file permissions --- src/Composer/Command/SelfUpdateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 0687de161..eb0de083e 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -173,7 +173,7 @@ EOT protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null) { try { - @chmod($newFilename, 0777 & ~umask()); + @chmod($newFilename, fileperms($localFilename)); if (!ini_get('phar.readonly')) { // test the phar validity $phar = new \Phar($newFilename); From 76a0e818b5cb9feae9e1d799e8bf1591cabf389d Mon Sep 17 00:00:00 2001 From: Josh Pollock Date: Wed, 28 Jan 2015 21:33:42 -0500 Subject: [PATCH 622/638] Improve notice about /usr/local/bin --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index a927f35fb..714b55be0 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -107,7 +107,7 @@ mv composer.phar /usr/local/bin/composer > **Note:** If the above fails due to permissions, run the `mv` line > again with sudo. -> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you recive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/composer/` manually. +> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you receive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/` manually before proceeding. Then, just run `composer` in order to run Composer instead of `php composer.phar`. From 0b2a31a89de99738096cc3a24f46c3266631c15d Mon Sep 17 00:00:00 2001 From: shaddag Date: Thu, 29 Jan 2015 10:20:31 +0100 Subject: [PATCH 623/638] suppress the prefix --- src/Composer/Command/HomeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index fe7929839..8501ef65f 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -84,7 +84,7 @@ EOT } if ($input->getOption('show')) { - $output->writeln(sprintf('%s: %s', $input->getOption('homepage') ? 'Homepage URL' : 'Repository URL', $url).' '); + $output->writeln(sprintf('%s', $url)); } else { $this->openBrowser($url); } From 80d7ab57ed215f443a03f770ea9f86fbc1fa9372 Mon Sep 17 00:00:00 2001 From: davidverholen Date: Thu, 29 Jan 2015 15:46:11 +0100 Subject: [PATCH 624/638] add check for remote Repository in GitDriver::supports --- src/Composer/Repository/Vcs/GitDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index da87e2e7d..b8aa31298 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -241,7 +241,11 @@ class GitDriver extends VcsDriver return false; } - // TODO try to connect to the server + $process = new ProcessExecutor($io); + if($process->execute('git ls-remote ' . $url, $output) === 0) { + return true; + } + return false; } } From 50d068b977128b19b67966c4eeb002ebb4184389 Mon Sep 17 00:00:00 2001 From: davidverholen Date: Thu, 29 Jan 2015 17:08:32 +0100 Subject: [PATCH 625/638] + limit git ls-remote to heads + escape repo url --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index b8aa31298..298639bc0 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -242,7 +242,7 @@ class GitDriver extends VcsDriver } $process = new ProcessExecutor($io); - if($process->execute('git ls-remote ' . $url, $output) === 0) { + if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) { return true; } From d650f05ddf7933a1cf6f62b6f3e4bc8a737a9872 Mon Sep 17 00:00:00 2001 From: "brandung GmbH & Co. KG" Date: Thu, 29 Jan 2015 20:00:49 +0100 Subject: [PATCH 626/638] removed needless output param --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 298639bc0..807ab7294 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -242,7 +242,7 @@ class GitDriver extends VcsDriver } $process = new ProcessExecutor($io); - if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) { + if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url)) === 0) { return true; } From 0ec450540121cb5b682b31c88a256365a439ea20 Mon Sep 17 00:00:00 2001 From: TaeL Kim Date: Fri, 30 Jan 2015 16:39:55 +0900 Subject: [PATCH 627/638] remove unused statements --- src/Composer/Factory.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 3f0041a85..3290b51dc 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -17,11 +17,9 @@ use Composer\Json\JsonFile; use Composer\IO\IOInterface; use Composer\Package\Archiver; use Composer\Repository\RepositoryManager; -use Composer\Repository\RepositoryInterface; use Composer\Repository\WritableRepositoryInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; -use Composer\Util\Filesystem; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Composer\EventDispatcher\EventDispatcher; use Composer\Autoload\AutoloadGenerator; From 2bd9cef79906e8ce42ffe33db84258e336fedfb3 Mon Sep 17 00:00:00 2001 From: Paul Dragoonis Date: Fri, 30 Jan 2015 10:35:39 +0000 Subject: [PATCH 628/638] Satis grammar fix. --- doc/articles/handling-private-packages-with-satis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index 0ee0adbca..5fce5377b 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -66,7 +66,7 @@ constraint if you want really specific versions. } ``` -Once you did this, you just run `php bin/satis build `. +Once you've done this, you just run `php bin/satis build `. For example `php bin/satis build config.json web/` would read the `config.json` file and build a static repository inside the `web/` directory. From 955e3f776a274429f731a130fda71bd0b54ff0aa Mon Sep 17 00:00:00 2001 From: Hannes Van De Vreken Date: Fri, 30 Jan 2015 21:51:12 +0100 Subject: [PATCH 629/638] Add notion of autoloader skipping autoload-dev rules --- doc/03-cli.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index f9625eb2e..40478ee9b 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -87,7 +87,7 @@ resolution. installing a package, you can use `--dry-run`. This will simulate the installation and show you what would happen. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). -* **--no-dev:** Skip installing packages listed in `require-dev`. +* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules. * **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-plugins:** Disables plugins. @@ -130,7 +130,7 @@ php composer.phar update vendor/* fulfill these. * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). -* **--no-dev:** Skip installing packages listed in `require-dev`. +* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules. * **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-plugins:** Disables plugins. From 777f2e81a3eb08beffc00ee5e513e68899dbd21e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 31 Jan 2015 21:24:16 +0000 Subject: [PATCH 630/638] Show more info when a download fails --- src/Composer/Downloader/FileDownloader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 0c03f18ab..96bd57c06 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -90,10 +90,10 @@ class FileDownloader implements DownloaderInterface } catch (\Exception $e) { if ($this->io->isDebug()) { $this->io->write(''); - $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage()); + $this->io->write('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage()); } elseif (count($urls)) { $this->io->write(''); - $this->io->write(' Failed, trying the next URL'); + $this->io->write(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')'); } if (!count($urls)) { From 8b46880f42ace73aca885b4652c8c50c589ac082 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 31 Jan 2015 21:24:33 +0000 Subject: [PATCH 631/638] Avoid failing on composer show of lazy providers --- src/Composer/Repository/ComposerRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5409a8dd3..ad3c9996b 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -204,6 +204,11 @@ class ComposerRepository extends ArrayRepository $this->loadProviderListings($this->loadRootServerFile()); } + if ($this->lazyProvidersUrl) { + // Can not determine list of provided packages for lazy repositories + return array(); + } + if ($this->providersUrl) { return array_keys($this->providerListing); } From 27d211096c720fa81fd60694e68fd05c2c1410c2 Mon Sep 17 00:00:00 2001 From: Rob Date: Mon, 2 Feb 2015 16:31:31 +0100 Subject: [PATCH 632/638] chmod 644 src/Composer/Command/RemoveCommand.php --- src/Composer/Command/RemoveCommand.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/Composer/Command/RemoveCommand.php diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php old mode 100755 new mode 100644 From fb54429941c9afa95f2002b7a9e7d24609cbca7e Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Mon, 9 Feb 2015 17:21:20 +0100 Subject: [PATCH 633/638] solve edge case for `composer remove vendor/pkg` --- .../Test/Json/JsonManipulatorTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index e3285efed..fdbc90af2 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -595,6 +595,44 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase ); } + public function testRemoveSubNodeFromRequire() + { + $manipulator = new JsonManipulator('{ + "repositories": [ + { + "package": { + "require": { + "this/should-not-end-up-in-root-require": "~2.0" + } + } + } + ], + "require": { + "package/a": "*", + "package/b": "*", + "package/c": "*" + } +}'); + + $this->assertTrue($manipulator->removeSubNode('require', 'package/c')); + $this->assertEquals('{ + "repositories": [ + { + "package": { + "require": { + "this/should-not-end-up-in-root-require": "~2.0" + } + } + } + ], + "require": { + "package/a": "*", + "package/b": "*" + } +} +', $manipulator->getContents()); + } + public function testAddRepositoryCanInitializeEmptyRepositories() { $manipulator = new JsonManipulator('{ From 6c971c302848a81a93dd626e53e8769dcd1ff4b7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Feb 2015 19:11:32 +0000 Subject: [PATCH 634/638] Fix regex matching and add more tests for addSubNode, refs #3721, fixes #3716 --- src/Composer/Json/JsonManipulator.php | 56 ++++++++----- .../Test/Json/JsonManipulatorTest.php | 80 +++++++++++++++++-- 2 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index e719e3d6d..d30a68385 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -31,7 +31,7 @@ class JsonManipulator if (!self::$RECURSE_BLOCKS) { self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*'; self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*'; - self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"'; + self::$JSON_STRING = '"(?:[^\0-\x09\x0a-\x1f\\\\"]+|\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})*"'; self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})'; } @@ -139,12 +139,20 @@ class JsonManipulator } // main node content not match-able - $nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s'; - if (!$this->pregMatch($nodeRegex, $this->contents, $match)) { - return false; + $nodeRegex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'. + '('.preg_quote(JsonFile::encode($mainNode)).'\s*:\s*\{)('.self::$RECURSE_BLOCKS.')(\})(.*)}s'; + try { + if (!$this->pregMatch($nodeRegex, $this->contents, $match)) { + return false; + } + } catch (\RuntimeException $e) { + if ($e->getCode() === PREG_BACKTRACK_LIMIT_ERROR) { + return false; + } + throw $e; } - $children = $match[2]; + $children = $match[3]; // invalid match due to un-regexable content, abort if (!@json_decode('{'.$children.'}')) { @@ -184,7 +192,7 @@ class JsonManipulator $children = $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $children; } - $this->contents = preg_replace($nodeRegex, addcslashes('${1}'.$children.'$3', '\\'), $this->contents); + $this->contents = preg_replace($nodeRegex, addcslashes('${1}${2}'.$children.'${4}${5}', '\\'), $this->contents); return true; } @@ -199,15 +207,23 @@ class JsonManipulator } // no node content match-able - $nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s'; - if (!$this->pregMatch($nodeRegex, $this->contents, $match)) { - return false; + $nodeRegex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'. + '('.preg_quote(JsonFile::encode($mainNode)).'\s*:\s*\{)('.self::$RECURSE_BLOCKS.')(\})(.*)}s'; + try { + if (!$this->pregMatch($nodeRegex, $this->contents, $match)) { + return false; + } + } catch (\RuntimeException $e) { + if ($e->getCode() === PREG_BACKTRACK_LIMIT_ERROR) { + return false; + } + throw $e; } - $children = $match[2]; + $children = $match[3]; // invalid match due to un-regexable content, abort - if (!@json_decode('{'.$children.'}')) { + if (!@json_decode('{'.$children.'}', true)) { return false; } @@ -245,7 +261,7 @@ class JsonManipulator // no child data left, $name was the only key in if (!trim($childrenClean)) { - $this->contents = preg_replace($nodeRegex, '$1'.$this->newline.$this->indent.'}', $this->contents); + $this->contents = preg_replace($nodeRegex, '$1$2'.$this->newline.$this->indent.'$4$5', $this->contents); // we have a subname, so we restore the rest of $name if ($subName !== null) { @@ -260,12 +276,12 @@ class JsonManipulator $that = $this; $this->contents = preg_replace_callback($nodeRegex, function ($matches) use ($that, $name, $subName, $childrenClean) { if ($subName !== null) { - $curVal = json_decode('{'.$matches[2].'}', true); + $curVal = json_decode('{'.$matches[3].'}', true); unset($curVal[$name][$subName]); $childrenClean = substr($that->format($curVal, 0), 1, -1); } - return $matches[1] . $childrenClean . $matches[3]; + return $matches[1] . $matches[2] . $childrenClean . $matches[4] . $matches[5]; }, $this->contents); return true; @@ -352,17 +368,17 @@ class JsonManipulator if ($count === false) { switch (preg_last_error()) { case PREG_NO_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_NO_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_NO_ERROR', PREG_NO_ERROR); case PREG_INTERNAL_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_INTERNAL_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_INTERNAL_ERROR', PREG_INTERNAL_ERROR); case PREG_BACKTRACK_LIMIT_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_BACKTRACK_LIMIT_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_BACKTRACK_LIMIT_ERROR', PREG_BACKTRACK_LIMIT_ERROR); case PREG_RECURSION_LIMIT_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_RECURSION_LIMIT_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_RECURSION_LIMIT_ERROR', PREG_RECURSION_LIMIT_ERROR); case PREG_BAD_UTF8_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_ERROR', PREG_BAD_UTF8_ERROR); case PREG_BAD_UTF8_OFFSET_ERROR: - throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_OFFSET_ERROR'); + throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_OFFSET_ERROR', PREG_BAD_UTF8_OFFSET_ERROR); default: throw new \RuntimeException('Failed to execute regex: Unknown error'); } diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index fdbc90af2..d8e564346 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -576,18 +576,22 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase ), 'fails on deep arrays with borked texts' => array( '{ - "repositories": [{ - "package": { "bar": "ba[z" } - }] + "repositories": [ + { + "package": { "bar": "ba[z" } + } + ] }', 'bar', false ), 'fails on deep arrays with borked texts2' => array( '{ - "repositories": [{ - "package": { "bar": "ba]z" } - }] + "repositories": [ + { + "package": { "bar": "ba]z" } + } + ] }', 'bar', false @@ -603,6 +607,9 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase "package": { "require": { "this/should-not-end-up-in-root-require": "~2.0" + }, + "require-dev": { + "this/should-not-end-up-in-root-require-dev": "~2.0" } } } @@ -611,16 +618,23 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase "package/a": "*", "package/b": "*", "package/c": "*" + }, + "require-dev": { + "package/d": "*" } }'); $this->assertTrue($manipulator->removeSubNode('require', 'package/c')); + $this->assertTrue($manipulator->removeSubNode('require-dev', 'package/d')); $this->assertEquals('{ "repositories": [ { "package": { "require": { "this/should-not-end-up-in-root-require": "~2.0" + }, + "require-dev": { + "this/should-not-end-up-in-root-require-dev": "~2.0" } } } @@ -628,6 +642,60 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase "require": { "package/a": "*", "package/b": "*" + }, + "require-dev": { + } +} +', $manipulator->getContents()); + } + + public function testAddSubNodeInRequire() + { + $manipulator = new JsonManipulator('{ + "repositories": [ + { + "package": { + "require": { + "this/should-not-end-up-in-root-require": "~2.0" + }, + "require-dev": { + "this/should-not-end-up-in-root-require-dev": "~2.0" + } + } + } + ], + "require": { + "package/a": "*", + "package/b": "*" + }, + "require-dev": { + "package/d": "*" + } +}'); + + $this->assertTrue($manipulator->addSubNode('require', 'package/c', '*')); + $this->assertTrue($manipulator->addSubNode('require-dev', 'package/e', '*')); + $this->assertEquals('{ + "repositories": [ + { + "package": { + "require": { + "this/should-not-end-up-in-root-require": "~2.0" + }, + "require-dev": { + "this/should-not-end-up-in-root-require-dev": "~2.0" + } + } + } + ], + "require": { + "package/a": "*", + "package/b": "*", + "package/c": "*" + }, + "require-dev": { + "package/d": "*", + "package/e": "*" } } ', $manipulator->getContents()); From d02eb8760b0b43749b86662bd59a3e1c9672007a Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Tue, 10 Feb 2015 12:46:11 +0100 Subject: [PATCH 635/638] strict check, testcase(s) --- res/composer-schema.json | 3 +- .../Composer/Test/Json/ComposerSchemaTest.php | 90 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Json/ComposerSchemaTest.php diff --git a/res/composer-schema.json b/res/composer-schema.json index 4c40bdfb2..d9a99a710 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -284,7 +284,8 @@ }, "minimum-stability": { "type": ["string"], - "description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable." + "description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable.", + "pattern": "^dev|alpha|beta|rc|RC|stable$" }, "prefer-stable": { "type": ["boolean"], diff --git a/tests/Composer/Test/Json/ComposerSchemaTest.php b/tests/Composer/Test/Json/ComposerSchemaTest.php new file mode 100644 index 000000000..1b8805f48 --- /dev/null +++ b/tests/Composer/Test/Json/ComposerSchemaTest.php @@ -0,0 +1,90 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Json; + +use JsonSchema\Validator; + +/** + * @author Rob Bast + */ +class ComposerSchemaTest extends \PHPUnit_Framework_TestCase +{ + public function testRequiredProperties() + { + $json = '{ }'; + $this->assertEquals(array( + array('property' => '', 'message' => 'the property name is required'), + array('property' => '', 'message' => 'the property description is required'), + ), $this->check($json)); + + $json = '{ "name": "vendor/package" }'; + $this->assertEquals(array( + array('property' => '', 'message' => 'the property description is required'), + ), $this->check($json)); + + $json = '{ "description": "generic description" }'; + $this->assertEquals(array( + array('property' => '', 'message' => 'the property name is required'), + ), $this->check($json)); + } + + public function testMinimumStabilityValues() + { + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "" }'; + $this->assertEquals(array( + array( + 'property' => 'minimum-stability', + 'message' => 'does not match the regex pattern ^dev|alpha|beta|rc|RC|stable$' + ), + ), $this->check($json), 'empty string'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "dummy" }'; + $this->assertEquals(array( + array( + 'property' => 'minimum-stability', + 'message' => 'does not match the regex pattern ^dev|alpha|beta|rc|RC|stable$' + ), + ), $this->check($json), 'dummy'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "dev" }'; + $this->assertTrue($this->check($json), 'dev'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "alpha" }'; + $this->assertTrue($this->check($json), 'alpha'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "beta" }'; + $this->assertTrue($this->check($json), 'beta'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "rc" }'; + $this->assertTrue($this->check($json), 'rc lowercase'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "RC" }'; + $this->assertTrue($this->check($json), 'rc uppercase'); + + $json = '{ "name": "vendor/package", "description": "generic description", "minimum-stability": "stable" }'; + $this->assertTrue($this->check($json), 'stable'); + } + + private function check($json) + { + $schema = json_decode(file_get_contents(__DIR__ . '/../../../../res/composer-schema.json')); + $validator = new Validator(); + $validator->check(json_decode($json), $schema); + + if (!$validator->isValid()) { + return $validator->getErrors(); + } + + return true; + } +} From 2d9401fb13625eb000bbaa1f48aa74815fc44125 Mon Sep 17 00:00:00 2001 From: David Verholen Date: Tue, 10 Feb 2015 13:33:09 +0100 Subject: [PATCH 636/638] fix bug in GitDriver::supports for remote repo for some reason it does not work (in packagist) without the $output param. I don't get any error message here, maybe someone has an idea, why? Anyway, need this ;) --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 807ab7294..298639bc0 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -242,7 +242,7 @@ class GitDriver extends VcsDriver } $process = new ProcessExecutor($io); - if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url)) === 0) { + if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) { return true; } From a34335a9bb3bb258d31f9967da4823163a481755 Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Thu, 5 Feb 2015 14:46:14 +0100 Subject: [PATCH 637/638] github deprecation changes - added some tests - minor bug fixes discovered during testing - resolved two deprecations (rate limit api and authorizations api) - added some more comments to make the flow more understandable --- src/Composer/Command/DiagnoseCommand.php | 2 +- src/Composer/Util/GitHub.php | 196 ++++++------- .../Test/Repository/Vcs/GitHubDriverTest.php | 7 +- tests/Composer/Test/Util/GitHubTest.php | 264 ++++++++++++++++++ 4 files changed, 366 insertions(+), 103 deletions(-) create mode 100644 tests/Composer/Test/Util/GitHubTest.php diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 0d24d74e6..b1fcc8dbe 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -274,7 +274,7 @@ EOT $json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false)); $data = json_decode($json, true); - return $data['rate']; + return $data['resources']['core']; } catch (\Exception $e) { if ($e instanceof TransportException && $e->getCode() === 401) { return 'The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it'; diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 8624edc04..6008dc062 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -76,124 +76,128 @@ class GitHub */ public function authorizeOAuthInteractively($originUrl, $message = null) { - $attemptCounter = 0; - - $apiUrl = ('github.com' === $originUrl) ? 'api.github.com' : $originUrl . '/api/v3'; - if ($message) { $this->io->write($message); } - $this->io->write('The credentials will be swapped for an OAuth token stored in '.$this->config->getAuthConfigSource()->getName().', your password will not be stored'); + + $this->io->write(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName())); $this->io->write('To revoke access to this token you can visit https://github.com/settings/applications'); + + $otp = null; + $attemptCounter = 0; + while ($attemptCounter++ < 5) { try { - if (empty($otp) || !$this->io->hasAuthentication($originUrl)) { - $username = $this->io->ask('Username: '); - $password = $this->io->askAndHideAnswer('Password: '); - $otp = null; - - $this->io->setAuthentication($originUrl, $username, $password); - } - - // build up OAuth app name - $appName = 'Composer'; - if ($this->config->get('github-expose-hostname') === true && 0 === $this->process->execute('hostname', $output)) { - $appName .= ' on ' . trim($output); - } else { - $appName .= ' [' . date('YmdHis') . ']'; - } - - $headers = array(); - if ($otp) { - $headers = array('X-GitHub-OTP: ' . $otp); - } - - // try retrieving an existing token with the same name - $contents = null; - $auths = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( - 'retry-auth-failure' => false, - 'http' => array( - 'header' => $headers - ) - ))); - foreach ($auths as $auth) { - if ( - isset($auth['app']['name']) - && 0 === strpos($auth['app']['name'], $appName) - && $auth['app']['url'] === 'https://getcomposer.org/' - ) { - $this->io->write('An existing OAuth token for Composer is present and will be reused'); - - $contents['token'] = $auth['token']; - break; - } - } - - // no existing token, create one - if (empty($contents['token'])) { - $headers[] = 'Content-Type: application/json'; - - $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( - 'retry-auth-failure' => false, - 'http' => array( - 'method' => 'POST', - 'follow_location' => false, - 'header' => $headers, - 'content' => json_encode(array( - 'scopes' => array('repo'), - 'note' => $appName, - 'note_url' => 'https://getcomposer.org/', - )), - ) - ))); - $this->io->write('Token successfully created'); - } + $response = $this->createToken($originUrl, $otp); } catch (TransportException $e) { + // https://developer.github.com/v3/#authentication && https://developer.github.com/v3/auth/#working-with-two-factor-authentication + // 401 is bad credentials, or missing otp code + // 403 is max login attempts exceeded if (in_array($e->getCode(), array(403, 401))) { - // 401 when authentication was supplied, handle 2FA if required. - if ($this->io->hasAuthentication($originUrl)) { - $headerNames = array_map(function ($header) { - return strtolower(strstr($header, ':', true)); - }, $e->getHeaders()); - - if ($key = array_search('x-github-otp', $headerNames)) { - $headers = $e->getHeaders(); - list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); - - if ('required' === $required) { - $this->io->write('Two-factor Authentication'); - - if ('app' === $method) { - $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.'); - } - - if ('sms' === $method) { - $this->io->write('You have been sent an SMS message with an authentication code to verify your identity.'); - } - - $otp = $this->io->ask('Authentication Code: '); - - continue; - } + // in case of a 401, and authentication was previously provided + if (401 === $e->getCode() && $this->io->hasAuthentication($originUrl)) { + // check for the presence of otp headers and get otp code from user + $otp = $this->checkTwoFactorAuthentication($e->getHeaders()); + // if given, retry creating a token using the user provided code + if (null !== $otp) { + continue; } } - $this->io->write('Invalid credentials.'); + if (401 === $e->getCode()) { + $this->io->write('Bad credentials.'); + } else { + $this->io->write('Maximum number of login attempts exceeded. Please try again later.'); + } + + $this->io->write('You can also manually create a personal token at https://github.com/settings/applications'); + $this->io->write('Add it using "composer config github-oauth.github.com "'); + continue; } throw $e; } - $this->io->setAuthentication($originUrl, $contents['token'], 'x-oauth-basic'); - - // store value in user config + $this->io->setAuthentication($originUrl, $response['token'], 'x-oauth-basic'); $this->config->getConfigSource()->removeConfigSetting('github-oauth.'.$originUrl); - $this->config->getAuthConfigSource()->addConfigSetting('github-oauth.'.$originUrl, $contents['token']); + // store value in user config + $this->config->getAuthConfigSource()->addConfigSetting('github-oauth.'.$originUrl, $response['token']); return true; } throw new \RuntimeException("Invalid GitHub credentials 5 times in a row, aborting."); } + + private function createToken($originUrl, $otp = null) + { + if (null === $otp || !$this->io->hasAuthentication($originUrl)) { + $username = $this->io->ask('Username: '); + $password = $this->io->askAndHideAnswer('Password: '); + + $this->io->setAuthentication($originUrl, $username, $password); + } + + $headers = array('Content-Type: application/json'); + if ($otp) { + $headers[] = 'X-GitHub-OTP: ' . $otp; + } + + $note = 'Composer'; + if ($this->config->get('github-expose-hostname') === true && 0 === $this->process->execute('hostname', $output)) { + $note .= ' on ' . trim($output); + } + $note .= ' [' . date('YmdHis') . ']'; + + $apiUrl = ('github.com' === $originUrl) ? 'api.github.com' : $originUrl . '/api/v3'; + + $json = $this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'follow_location' => false, + 'header' => $headers, + 'content' => json_encode(array( + 'scopes' => array('repo'), + 'note' => $note, + 'note_url' => 'https://getcomposer.org/', + )), + ) + )); + + $this->io->write('Token successfully created'); + + return JsonFile::parseJson($json); + } + + private function checkTwoFactorAuthentication(array $headers) + { + $headerNames = array_map( + function ($header) { + return strtolower(strstr($header, ':', true)); + }, + $headers + ); + + if (false !== ($key = array_search('x-github-otp', $headerNames))) { + list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); + + if ('required' === $required) { + $this->io->write('Two-factor Authentication'); + + if ('app' === $method) { + $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.'); + } + + if ('sms' === $method) { + $this->io->write('You have been sent an SMS message with an authentication code to verify your identity.'); + } + + return $this->io->ask('Authentication Code: '); + } + } + + return null; + } } diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index d20da87dc..e180c5eec 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -79,16 +79,11 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('github.com'), $this->matchesRegularExpression('{someuser|abcdef}'), $this->matchesRegularExpression('{somepassword|x-oauth-basic}')); $remoteFilesystem->expects($this->at(1)) - ->method('getContents') - ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/authorizations'), $this->equalTo(false)) - ->will($this->returnValue('[]')); - - $remoteFilesystem->expects($this->at(2)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/authorizations'), $this->equalTo(false)) ->will($this->returnValue('{"token": "abcdef"}')); - $remoteFilesystem->expects($this->at(3)) + $remoteFilesystem->expects($this->at(2)) ->method('getContents') ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}')); diff --git a/tests/Composer/Test/Util/GitHubTest.php b/tests/Composer/Test/Util/GitHubTest.php new file mode 100644 index 000000000..4a1f3a35a --- /dev/null +++ b/tests/Composer/Test/Util/GitHubTest.php @@ -0,0 +1,264 @@ + +* 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\Downloader\TransportException; +use Composer\Util\GitHub; +use RecursiveArrayIterator; +use RecursiveIteratorIterator; + +/** +* @author Rob Bast +*/ +class GitHubTest extends \PHPUnit_Framework_TestCase +{ + private $username = 'username'; + private $password = 'password'; + private $authcode = 'authcode'; + private $message = 'mymessage'; + private $origin = 'github.com'; + private $token = 'githubtoken'; + + public function testUsernamePasswordAuthenticationFlow() + { + $io = $this->getIOMock(); + $io + ->expects($this->at(0)) + ->method('write') + ->with($this->message) + ; + $io + ->expects($this->once()) + ->method('ask') + ->with('Username: ') + ->willReturn($this->username) + ; + $io + ->expects($this->once()) + ->method('askAndHideAnswer') + ->with('Password: ') + ->willReturn($this->password) + ; + + $rfs = $this->getRemoteFilesystemMock(); + $rfs + ->expects($this->once()) + ->method('getContents') + ->with( + $this->equalTo($this->origin), + $this->equalTo(sprintf('https://api.%s/authorizations', $this->origin)), + $this->isFalse(), + $this->anything() + ) + ->willReturn(sprintf('{"token": "%s"}', $this->token)) + ; + + $config = $this->getConfigMock(); + $config + ->expects($this->exactly(2)) + ->method('getAuthConfigSource') + ->willReturn($this->getAuthJsonMock()) + ; + $config + ->expects($this->once()) + ->method('getConfigSource') + ->willReturn($this->getConfJsonMock()) + ; + + $github = new GitHub($io, $config, null, $rfs); + + $this->assertTrue($github->authorizeOAuthInteractively($this->origin, $this->message)); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Invalid GitHub credentials 5 times in a row, aborting. + */ + public function testUsernamePasswordFailure() + { + $io = $this->getIOMock(); + $io + ->expects($this->exactly(5)) + ->method('ask') + ->with('Username: ') + ->willReturn($this->username) + ; + $io + ->expects($this->exactly(5)) + ->method('askAndHideAnswer') + ->with('Password: ') + ->willReturn($this->password) + ; + + $rfs = $this->getRemoteFilesystemMock(); + $rfs + ->expects($this->exactly(5)) + ->method('getContents') + ->will($this->throwException(new TransportException('', 401))) + ; + + $config = $this->getConfigMock(); + $config + ->expects($this->exactly(1)) + ->method('getAuthConfigSource') + ->willReturn($this->getAuthJsonMock()) + ; + + $github = new GitHub($io, $config, null, $rfs); + + $github->authorizeOAuthInteractively($this->origin); + } + + public function testTwoFactorAuthentication() + { + $io = $this->getIOMock(); + $io + ->expects($this->exactly(2)) + ->method('hasAuthentication') + ->will($this->onConsecutiveCalls(true, true)) + ; + $io + ->expects($this->exactly(2)) + ->method('ask') + ->withConsecutive( + array('Username: '), + array('Authentication Code: ') + ) + ->will($this->onConsecutiveCalls($this->username, $this->authcode)) + ; + $io + ->expects($this->once()) + ->method('askAndHideAnswer') + ->with('Password: ') + ->willReturn($this->password) + ; + + $exception = new TransportException('', 401); + $exception->setHeaders(array('X-GitHub-OTP: required; app')); + + $rfs = $this->getRemoteFilesystemMock(); + $rfs + ->expects($this->at(0)) + ->method('getContents') + ->will($this->throwException($exception)) + ; + $rfs + ->expects($this->at(1)) + ->method('getContents') + ->with( + $this->equalTo($this->origin), + $this->equalTo(sprintf('https://api.%s/authorizations', $this->origin)), + $this->isFalse(), + $this->callback(function ($array) { + $headers = GitHubTest::recursiveFind($array, 'header'); + foreach ($headers as $string) { + if ('X-GitHub-OTP: authcode' === $string) { + return true; + } + } + return false; + }) + ) + ->willReturn(sprintf('{"token": "%s"}', $this->token)) + ; + + $config = $this->getConfigMock(); + $config + ->expects($this->atLeastOnce()) + ->method('getAuthConfigSource') + ->willReturn($this->getAuthJsonMock()) + ; + $config + ->expects($this->atLeastOnce()) + ->method('getConfigSource') + ->willReturn($this->getConfJsonMock()) + ; + + $github = new GitHub($io, $config, null, $rfs); + + $this->assertTrue($github->authorizeOAuthInteractively($this->origin)); + } + + private function getIOMock() + { + $io = $this + ->getMockBuilder('Composer\IO\ConsoleIO') + ->disableOriginalConstructor() + ->getMock() + ; + + return $io; + } + + private function getConfigMock() + { + $config = $this->getMock('Composer\Config'); + + return $config; + } + + private function getRemoteFilesystemMock() + { + $rfs = $this + ->getMockBuilder('Composer\Util\RemoteFilesystem') + ->disableOriginalConstructor() + ->getMock() + ; + + return $rfs; + } + + private function getAuthJsonMock() + { + $authjson = $this + ->getMockBuilder('Composer\Config\JsonConfigSource') + ->disableOriginalConstructor() + ->getMock() + ; + $authjson + ->expects($this->atLeastOnce()) + ->method('getName') + ->willReturn('auth.json') + ; + + return $authjson; + } + + private function getConfJsonMock() + { + $confjson = $this + ->getMockBuilder('Composer\Config\JsonConfigSource') + ->disableOriginalConstructor() + ->getMock() + ; + $confjson + ->expects($this->atLeastOnce()) + ->method('removeConfigSetting') + ->with('github-oauth.'.$this->origin) + ; + + return $confjson; + } + + public static function recursiveFind($array, $needle) + { + $iterator = new RecursiveArrayIterator($array); + $recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST); + + foreach ($recursive as $key => $value) { + if ($key === $needle) { + return $value; + } + } + } +} From 16578d1d01656ce7b694abd5517af44395cc53b3 Mon Sep 17 00:00:00 2001 From: Francis Besset Date: Wed, 11 Feb 2015 11:40:41 +0100 Subject: [PATCH 638/638] Simplified syntax --- src/Composer/Json/JsonValidationException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonValidationException.php b/src/Composer/Json/JsonValidationException.php index 5d21f533c..2fa5eb489 100644 --- a/src/Composer/Json/JsonValidationException.php +++ b/src/Composer/Json/JsonValidationException.php @@ -21,7 +21,7 @@ class JsonValidationException extends Exception { protected $errors; - public function __construct($message, $errors = array(), \Exception $previous = null) + public function __construct($message, $errors = array(), Exception $previous = null) { $this->errors = $errors; parent::__construct($message, 0, $previous);