From b999d18365a8dc0a5b2478d1edaa9b6b148cf05f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 10:51:57 +0200 Subject: [PATCH 001/101] Improve include_paths generation, fixes #596 --- src/Composer/Autoload/AutoloadGenerator.php | 70 ++++++++++++------- .../Test/Autoload/AutoloadGeneratorTest.php | 12 +++- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index daf2827a7..b9806320c 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -34,8 +34,8 @@ class AutoloadGenerator $relVendorPath = $filesystem->findShortestPath(getcwd(), $vendorPath, true); $vendorDirCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); - $appBaseDir = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); - $appBaseDir = str_replace('__DIR__', '$vendorDir', $appBaseDir); + $appBaseDirCode = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); + $appBaseDirCode = str_replace('__DIR__', '$vendorDir', $appBaseDirCode); $namespacesFile = << $paths) { $exportedPaths = array(); foreach ($paths as $path) { - $path = strtr($path, '\\', '/'); - $baseDir = ''; - if (!$filesystem->isAbsolutePath($path)) { - if (strpos($path, $relVendorPath) === 0) { - // path starts with vendor dir - $path = substr($path, strlen($relVendorPath)); - $baseDir = '$vendorDir . '; - } else { - $path = '/'.$path; - $baseDir = '$baseDir . '; - } - } elseif (strpos($path, $vendorPath) === 0) { - $path = substr($path, strlen($vendorPath)); - $baseDir = '$vendorDir . '; - } - $exportedPaths[] = $baseDir.var_export($path, true); + $exportedPaths[] = $this->getPathCode($filesystem, $relVendorPath, $vendorPath, $path); } $exportedPrefix = var_export($namespace, true); $namespacesFile .= " $exportedPrefix => "; @@ -88,7 +73,7 @@ EOF; // autoload_classmap.php generated by Composer \$vendorDir = $vendorDirCode; -\$baseDir = $appBaseDir; +\$baseDir = $appBaseDirCode; return array( @@ -106,7 +91,7 @@ EOF; file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile); file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile); - if ($includePathFile = $this->getIncludePathsFile($packageMap)) { + if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorDirCode, $appBaseDirCode)) { file_put_contents($targetDir.'/include_paths.php', $includePathFile); } file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile)); @@ -186,7 +171,7 @@ EOF; return $loader; } - protected function getIncludePathsFile(array $packageMap) + protected function getIncludePathsFile(array $packageMap, Filesystem $filesystem, $relVendorPath, $vendorPath, $vendorDirCode, $appBaseDirCode) { $includePaths = array(); @@ -198,6 +183,7 @@ EOF; } foreach ($package->getIncludePaths() as $includePath) { + $includePath = trim($includePath, '/'); $includePaths[] = empty($installPath) ? $includePath : $installPath.'/'.$includePath; } } @@ -206,9 +192,43 @@ EOF; return; } - return sprintf( - "getPathCode($filesystem, $relVendorPath, $vendorPath, $path) . ",\n"; + } + + return $includePathsFile . ");\n"; + } + + protected function getPathCode(Filesystem $filesystem, $relVendorPath, $vendorPath, $path) + { + $path = strtr($path, '\\', '/'); + $baseDir = ''; + if (!$filesystem->isAbsolutePath($path)) { + if (strpos($path, $relVendorPath) === 0) { + // path starts with vendor dir + $path = substr($path, strlen($relVendorPath)); + $baseDir = '$vendorDir . '; + } else { + $path = '/'.$path; + $baseDir = '$baseDir . '; + } + } elseif (strpos($path, $vendorPath) === 0) { + $path = substr($path, strlen($vendorPath)); + $baseDir = '$vendorDir . '; + } + return $baseDir.var_export($path, true); } protected function getAutoloadFile($usePSR0, $useClassMap, $useIncludePath) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index dced4ef7f..f4feef203 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -264,8 +264,12 @@ class AutoloadGeneratorTest extends TestCase $b = new MemoryPackage("b/b", "1.0", "1.0"); $b->setIncludePaths(array("library")); + $c = new MemoryPackage("c", "1.0", "1.0"); + $c->setIncludePaths(array("library")); + $packages[] = $a; $packages[] = $b; + $packages[] = $c; $this->repository->expects($this->once()) ->method("getPackages") @@ -275,10 +279,12 @@ class AutoloadGeneratorTest extends TestCase $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); + $this->assertFileEquals(__DIR__.'/Fixtures/include_paths.php', $this->vendorDir.'/.composer/include_paths.php'); $this->assertEquals( array( - $this->vendorDir."/a/a/lib/", - $this->vendorDir."/b/b/library" + $this->vendorDir."/a/a/lib", + $this->vendorDir."/b/b/library", + $this->vendorDir."/c/library", ), require($this->vendorDir."/.composer/include_paths.php") ); @@ -307,7 +313,7 @@ class AutoloadGeneratorTest extends TestCase require($this->vendorDir."/.composer/autoload.php"); $this->assertEquals( - $oldIncludePath.PATH_SEPARATOR.$this->vendorDir."/a/a/lib/", + $oldIncludePath.PATH_SEPARATOR.$this->vendorDir."/a/a/lib", get_include_path() ); From 120171ff5a8dd356e908010462f6635e51fcda91 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 19:45:26 +0200 Subject: [PATCH 002/101] Fix create project --- src/Composer/Command/CreateProjectCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 69e083b3b..cf45d0ab9 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -18,6 +18,7 @@ use Composer\Installer\ProjectInstaller; use Composer\IO\IOInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\FilesystemRepository; +use Composer\Repository\InstalledFilesystemRepository; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -113,7 +114,7 @@ EOT $io->write('Installing ' . $package->getName() . ' as new project.', true); $projectInstaller = new ProjectInstaller($directory, $dm); - $projectInstaller->install(new FilesystemRepository(new JsonFile('php://memory')), $package); + $projectInstaller->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), $package); $io->write('Created project into directory ' . $directory . '', true); chdir($directory); From ada8d69507e16dbbbd84841156d3d5830a48e208 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 19:55:56 +0200 Subject: [PATCH 003/101] Update satis docs --- .../handling-private-packages-with-satis.md | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index a91123f90..72fa0a8cc 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -5,7 +5,8 @@ Satis 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). +[GitHub](http://github.com/composer/satis) or install via CLI: +`composer create-project composer/satis`. ## Setup @@ -13,12 +14,27 @@ For example let's assume you have a few packages you want to reuse across your company but don't really want to open-source. You would first define a Satis configuration file, which is basically a stripped-down version of a `composer.json` file. It contains a few repositories, and then you use the require -key to say which packages it should dump in the static repository it creates. +key to say which packages it should dump in the static repository it creates, or +use require-all to select all of them. Here is an example configuration, you see that it holds a few VCS repositories, -but those could be any types of [repositories](../05-repositories.md). Then -the require just lists all the packages we need, using a `"*"` constraint to -make sure all versions are selected. +but those could be any types of [repositories](../05-repositories.md). Then it +uses `"require-all": true` which selects all versions of all packages in the +repositories you defined. + + { + "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": [ @@ -29,7 +45,7 @@ make sure all versions are selected. "require": { "company/package": "*", "company/package2": "*", - "company/package3": "*" + "company/package3": "2.0.0" } } From 4a55d85a2d8ca03734039fe3cfae57558d96bab7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 20:03:09 +0200 Subject: [PATCH 004/101] Respect umask in chmod calls --- src/Composer/Command/SelfUpdateCommand.php | 2 +- src/Composer/Installer/LibraryInstaller.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index f741beeca..16f92babd 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -53,7 +53,7 @@ EOT $rfs->copy('getcomposer.org', $remoteFilename, $tempFilename); try { - chmod($tempFilename, 0755); + chmod($tempFilename, 0777 & ~umask()); // test the phar validity $phar = new \Phar($tempFilename); // free the variable to unlock the file diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 9e321df19..d690c357b 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -152,7 +152,7 @@ class LibraryInstaller implements InstallerInterface // likely leftover from a previous install, make sure // that the target is still executable in case this // is a fresh install of the vendor. - chmod($link, 0755); + chmod($link, 0777 & ~umask()); } $this->io->write('Skipped installation of '.$bin.' for package '.$package->getName().', name conflicts with an existing file'); continue; @@ -163,7 +163,7 @@ class LibraryInstaller implements InstallerInterface // add unixy support for cygwin and similar environments if ('.bat' !== substr($bin, -4)) { file_put_contents($link, $this->generateUnixyProxyCode($bin, $link)); - chmod($link, 0755); + chmod($link, 0777 & ~umask()); $link .= '.bat'; } file_put_contents($link, $this->generateWindowsProxyCode($bin, $link)); @@ -177,7 +177,7 @@ class LibraryInstaller implements InstallerInterface } } - chmod($link, 0755); + chmod($link, 0777 & ~umask()); } } From 244a7fd37b1b78935e5a64f76e2652412978f332 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 20:04:38 +0200 Subject: [PATCH 005/101] Add missing test fixture --- .../Test/Autoload/Fixtures/include_paths.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/Composer/Test/Autoload/Fixtures/include_paths.php diff --git a/tests/Composer/Test/Autoload/Fixtures/include_paths.php b/tests/Composer/Test/Autoload/Fixtures/include_paths.php new file mode 100644 index 000000000..37ac66c87 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/include_paths.php @@ -0,0 +1,12 @@ + Date: Thu, 19 Apr 2012 20:50:24 +0200 Subject: [PATCH 006/101] Fix 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 72fa0a8cc..c2e96ebc9 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -6,7 +6,7 @@ Satis 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 create-project composer/satis`. +`composer.phar create-project composer/satis`. ## Setup From 22fe296ad0ff789adf0cec163281836b8a69f221 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 21:55:35 +0200 Subject: [PATCH 007/101] Move .composer files out into the vendor dir, fixes #497 --- CHANGELOG.md | 1 + doc/00-intro.md | 2 +- doc/01-basic-usage.md | 10 +++++----- src/Composer/Autoload/AutoloadGenerator.php | 13 ++++++++++++- src/Composer/Compiler.php | 8 ++++---- src/Composer/Factory.php | 11 +++++++++-- src/Composer/Installer.php | 4 ++-- src/bootstrap.php | 2 +- 8 files changed, 35 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09f21b61b..6b32d8fcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev * Schema: Removed 'recommend' * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint + * Break: .composer/autoload.php and other files in vendor/.composer have been moved to vendor/ * Added caching of repository metadata (faster startup times & failover if packagist is down) * Added include_path support for legacy projects that are full of require_once statements * Added installation notifications API to allow better statistics on Composer repositories diff --git a/doc/00-intro.md b/doc/00-intro.md index 2437b7437..91ee3e66b 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -80,7 +80,7 @@ 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/.composer/autoload.php'; + require 'vendor/autoload.php'; Woh! 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 27f4d2972..b920eb455 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -136,10 +136,10 @@ but it makes life quite a bit simpler. ## Autoloading For libraries that specify autoload information, Composer generates a -`vendor/.composer/autoload.php` file. You can simply include this file and you +`vendor/autoload.php` file. You can simply include this file and you will get autoloading for free. - require 'vendor/.composer/autoload.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 @@ -168,13 +168,13 @@ be in your project root. An example filename would be `src/Acme/Foo.php` containing an `Acme\Foo` class. After adding the `autoload` field, you have to re-run `install` to re-generate -the `vendor/.composer/autoload.php` file. +the `vendor/autoload.php` file. 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/.composer/autoload.php'; + $loader = require 'vendor/autoload.php'; $loader->add('Acme\Test', __DIR__); In addition to PSR-0 autoloading, classmap is also supported. This allows @@ -182,7 +182,7 @@ classes to be autoloaded even if they do not conform to PSR-0. See the [autoload reference](04-schema.md#autoload) for more details. > **Note:** Composer provides its own autoloader. If you don't want to use -that one, you can just include `vendor/.composer/autoload_namespaces.php`, +that one, you can just include `vendor/autoload_namespaces.php`, which returns an associative array mapping namespaces to directories. ← [Intro](00-intro.md) | [Libraries](02-libraries.md) → diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index b9806320c..2e8fc07eb 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -25,7 +25,7 @@ use Composer\Util\Filesystem; */ class AutoloadGenerator { - public function dump(RepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir) + public function dump(RepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $bcLinks = false) { $filesystem = new Filesystem(); $filesystem->ensureDirectoryExists($installationManager->getVendorPath()); @@ -96,6 +96,17 @@ EOF; } file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile)); copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); + + // TODO BC feature, add E_DEPRECATED in autoload.php on April 30th, remove after May 30th + if ($bcLinks) { + file_put_contents($targetDir.'/.composer/autoload_namespaces.php', "addFile($phar, $file); } - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/ClassLoader.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_namespaces.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_classmap.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/ClassLoader.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload_namespaces.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload_classmap.php')); $this->addComposerBin($phar); // Stubs diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 9758fc946..e166754c5 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -151,8 +151,15 @@ class Factory protected function addLocalRepository(RepositoryManager $rm, $vendorDir) { - $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); - $rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed_dev.json'))); + // TODO BC feature, remove after May 30th + if (file_exists($vendorDir.'/.composer/installed.json')) { + rename($vendorDir.'/.composer/installed.json', $vendorDir.'/installed.json'); + } + if (file_exists($vendorDir.'/.composer/installed_dev.json')) { + rename($vendorDir.'/.composer/installed_dev.json', $vendorDir.'/installed_dev.json'); + } + $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/installed.json'))); + $rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/installed_dev.json'))); } protected function addPackagistRepository(array $localConfig) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 1816e7ee2..1b0b78835 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -152,7 +152,7 @@ class Installer } } - // dump suggestions + // output suggestions foreach ($this->suggestedPackages as $suggestion) { $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); } @@ -174,7 +174,7 @@ class Installer $this->io->write('Generating autoload files'); $generator = new AutoloadGenerator; $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); - $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); + $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath(), true); // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; diff --git a/src/bootstrap.php b/src/bootstrap.php index c7f2fd0d6..0eab8a25e 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -16,7 +16,7 @@ function includeIfExists($file) { } } -if ((!$loader = includeIfExists(__DIR__.'/../vendor/.composer/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../.composer/autoload.php'))) { +if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) { die('You must set up the project dependencies, run the following commands:'.PHP_EOL. 'curl -s http://getcomposer.org/installer | php'.PHP_EOL. 'php composer.phar install'.PHP_EOL); From 32643581ba96f8f9b5a01864f5b702485366b27c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 21:56:05 +0200 Subject: [PATCH 008/101] Fix line endings --- CHANGELOG.md | 58 +++++++++++++++++++++++------------------------ src/bootstrap.php | 50 ++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b32d8fcb..e11ed2552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,29 @@ -* 1.0.0-alpha3 - - * Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev - * Schema: Removed 'recommend' - * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint - * Break: .composer/autoload.php and other files in vendor/.composer have been moved to vendor/ - * Added caching of repository metadata (faster startup times & failover if packagist is down) - * Added include_path support for legacy projects that are full of require_once statements - * Added installation notifications API to allow better statistics on Composer repositories - * Improved repository protocol to have large cacheable parts - -* 1.0.0-alpha2 (2012-04-03) - - * Added `create-project` command to install a project from scratch with composer - * Added automated `classmap` autoloading support for non-PSR-0 compliant projects - * Added human readable error reporting when deps can not be solved - * Added support for private GitHub and SVN repositories (use --no-interaction for CI) - * Added "file" downloader type to download plain files - * Added support for authentication with svn repositories - * Added autoload support for PEAR repositories - * Improved clones from GitHub which now automatically select between git/https/http protocols - * Improved `validate` command to give more feedback - * Improved the `search` & `show` commands output - * Removed dependency on filter_var - * Various robustness & error handling improvements, docs fixes and more bug fixes - -* 1.0.0-alpha1 (2012-03-01) - - * Initial release +* 1.0.0-alpha3 + + * Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev + * Schema: Removed 'recommend' + * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint + * Break: .composer/autoload.php and other files in vendor/.composer have been moved to vendor/ + * Added caching of repository metadata (faster startup times & failover if packagist is down) + * Added include_path support for legacy projects that are full of require_once statements + * Added installation notifications API to allow better statistics on Composer repositories + * Improved repository protocol to have large cacheable parts + +* 1.0.0-alpha2 (2012-04-03) + + * Added `create-project` command to install a project from scratch with composer + * Added automated `classmap` autoloading support for non-PSR-0 compliant projects + * Added human readable error reporting when deps can not be solved + * Added support for private GitHub and SVN repositories (use --no-interaction for CI) + * Added "file" downloader type to download plain files + * Added support for authentication with svn repositories + * Added autoload support for PEAR repositories + * Improved clones from GitHub which now automatically select between git/https/http protocols + * Improved `validate` command to give more feedback + * Improved the `search` & `show` commands output + * Removed dependency on filter_var + * Various robustness & error handling improvements, docs fixes and more bug fixes + +* 1.0.0-alpha1 (2012-03-01) + + * Initial release diff --git a/src/bootstrap.php b/src/bootstrap.php index 0eab8a25e..3ce95752c 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -1,25 +1,25 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -function includeIfExists($file) { - if (file_exists($file)) { - return include $file; - } -} - -if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) { - die('You must set up the project dependencies, run the following commands:'.PHP_EOL. - 'curl -s http://getcomposer.org/installer | php'.PHP_EOL. - 'php composer.phar install'.PHP_EOL); -} - -return $loader; \ No newline at end of file + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +function includeIfExists($file) { + if (file_exists($file)) { + return include $file; + } +} + +if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) { + die('You must set up the project dependencies, run the following commands:'.PHP_EOL. + 'curl -s http://getcomposer.org/installer | php'.PHP_EOL. + 'php composer.phar install'.PHP_EOL); +} + +return $loader; From 66135538c1295c2076262373785a11d81a8f4272 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 21:26:01 +0200 Subject: [PATCH 009/101] Allow root packages packages with target-dir to be autoloaded, fixes #139 --- src/Composer/Autoload/AutoloadGenerator.php | 36 ++++++++++++++++- .../Test/Autoload/AutoloadGeneratorTest.php | 16 ++++++++ .../Autoload/Fixtures/autoload_target_dir.php | 39 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2e8fc07eb..8b32cc181 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -79,6 +79,36 @@ return array( EOF; + // add custom psr-0 autoloading if the root package has a target dir + $targetDirLoader = null; + $mainAutoload = $mainPackage->getAutoload(); + if ($mainPackage->getTargetDir() && $mainAutoload['psr-0']) { + $levels = count(explode('/', trim(strtr($mainPackage->getTargetDir(), '\\', '/'), '/'))); + $prefixes = implode("', '", array_map(function ($prefix) { + return var_export($prefix, true); + }, array_keys($mainAutoload['psr-0']))); + $baseDirFromTargetDirCode = $filesystem->findShortestPathCode(realpath($targetDir), getcwd(), true); + + $targetDirLoader = <<getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorDirCode, $appBaseDirCode)) { file_put_contents($targetDir.'/include_paths.php', $includePathFile); } - file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile)); + file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile, $targetDirLoader)); copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); // TODO BC feature, add E_DEPRECATED in autoload.php on April 30th, remove after May 30th @@ -242,7 +272,7 @@ EOF; return $baseDir.var_export($path, true); } - protected function getAutoloadFile($usePSR0, $useClassMap, $useIncludePath) + protected function getAutoloadFile($usePSR0, $useClassMap, $useIncludePath, $targetDirLoader) { $file = <<<'HEADER' register(); diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index f4feef203..089b40adb 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -141,6 +141,22 @@ class AutoloadGeneratorTest extends TestCase $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/.composer', 'classmap'); } + public function testMainPackageAutoloadingWithTargetDir() + { + $package = new MemoryPackage('a', '1.0', '1.0'); + $package->setAutoload(array( + 'psr-0' => array('Main\\Foo' => ''), + )); + $package->setTargetDir('Main/Foo'); + + $this->repository->expects($this->once()) + ->method('getPackages') + ->will($this->returnValue(array())); + + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); + $this->assertFileEquals(__DIR__.'/Fixtures/autoload_target_dir.php', $this->vendorDir.'/.composer/autoload.php'); + } + public function testVendorsAutoloading() { $package = new MemoryPackage('a', '1.0', '1.0'); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php new file mode 100644 index 000000000..8e70900f0 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php @@ -0,0 +1,39 @@ + $path) { + $loader->add($namespace, $path); + } + + $classMap = require __DIR__.'/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + spl_autoload_register(function($class) { + $prefixes = array('Main\\Foo'); + foreach ($prefixes as $prefix) { + if (0 !== strpos($class, $prefix)) { + continue; + } + $path = dirname(dirname(__DIR__)) . '/' . implode('/', array_slice(explode('\\', $class), 2)).'.php'; + if (!stream_resolve_include_path($path)) { + return false; + } + require_once $path; + return true; + } + }); + + $loader->register(); + + return $loader; +}); From a9176aceccb6d7c887f6ac920414c67cf3ef23c5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 21:27:33 +0200 Subject: [PATCH 010/101] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e11ed2552..306521056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Added caching of repository metadata (faster startup times & failover if packagist is down) * Added include_path support for legacy projects that are full of require_once statements * Added installation notifications API to allow better statistics on Composer repositories + * Added autoloading support for root packages that use target-dir * Improved repository protocol to have large cacheable parts * 1.0.0-alpha2 (2012-04-03) From 51711c2f73e0382e0394049356d0d8ca4bb77abb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 22:04:49 +0200 Subject: [PATCH 011/101] Fix multi-prefix handling --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 4 ++-- tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 8b32cc181..38421b375 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -84,7 +84,7 @@ EOF; $mainAutoload = $mainPackage->getAutoload(); if ($mainPackage->getTargetDir() && $mainAutoload['psr-0']) { $levels = count(explode('/', trim(strtr($mainPackage->getTargetDir(), '\\', '/'), '/'))); - $prefixes = implode("', '", array_map(function ($prefix) { + $prefixes = implode(', ', array_map(function ($prefix) { return var_export($prefix, true); }, array_keys($mainAutoload['psr-0']))); $baseDirFromTargetDirCode = $filesystem->findShortestPathCode(realpath($targetDir), getcwd(), true); diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 089b40adb..5cabfd2df 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -145,9 +145,9 @@ class AutoloadGeneratorTest extends TestCase { $package = new MemoryPackage('a', '1.0', '1.0'); $package->setAutoload(array( - 'psr-0' => array('Main\\Foo' => ''), + 'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''), )); - $package->setTargetDir('Main/Foo'); + $package->setTargetDir('Main/Foo/'); $this->repository->expects($this->once()) ->method('getPackages') diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php index 8e70900f0..f2acfcc3b 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php @@ -19,7 +19,7 @@ return call_user_func(function() { } spl_autoload_register(function($class) { - $prefixes = array('Main\\Foo'); + $prefixes = array('Main\\Foo', 'Main\\Bar'); foreach ($prefixes as $prefix) { if (0 !== strpos($class, $prefix)) { continue; From ab0ced493c38a231ed71038d9de4a463ca11fba3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 19 Apr 2012 22:26:09 +0200 Subject: [PATCH 012/101] Make sure BC dir exists --- src/Composer/Autoload/AutoloadGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2e8fc07eb..d3267e9b1 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -99,6 +99,7 @@ EOF; // TODO BC feature, add E_DEPRECATED in autoload.php on April 30th, remove after May 30th if ($bcLinks) { + $filesystem->ensureDirectoryExists($targetDir.'/.composer'); file_put_contents($targetDir.'/.composer/autoload_namespaces.php', " Date: Fri, 20 Apr 2012 20:08:38 +0200 Subject: [PATCH 013/101] Show real version instead of self.version in suggest --- src/Composer/Package/Loader/ArrayLoader.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 0e706bad9..ae5678375 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -166,6 +166,11 @@ class ArrayLoader } if (isset($config['suggest']) && is_array($config['suggest'])) { + foreach ($config['suggest'] as $target => $reason) { + if ('self.version' === trim($reason)) { + $config['suggest'][$target] = $package->getPrettyVersion(); + } + } $package->setSuggests($config['suggest']); } From 06fb1b80831f49900becc70df0b5358ebdb9ec1f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 20 Apr 2012 22:10:58 +0200 Subject: [PATCH 014/101] Fix priority of aliases when mixing root package aliases with branch-alias ones, fixes #576 --- .../DependencyResolver/DefaultPolicy.php | 38 ++++++++++++++++++- src/Composer/Installer.php | 6 ++- src/Composer/Package/AliasPackage.php | 22 +++++++++++ .../DependencyResolver/DefaultPolicyTest.php | 30 +++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 3659b0a51..9b77e8974 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -69,6 +69,8 @@ class DefaultPolicy implements PolicyInterface $literals = $this->pruneToBestVersion($literals); $literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals); + + $literals = $this->pruneToLocalAliases($literals); } $selected = call_user_func_array('array_merge', $packages); @@ -210,8 +212,8 @@ class DefaultPolicy implements PolicyInterface } /** - * Assumes that installed packages come first and then all highest priority packages - */ + * Assumes that installed packages come first and then all highest priority packages + */ protected function pruneToHighestPriorityOrInstalled(Pool $pool, array $installedMap, array $literals) { $selected = array(); @@ -239,4 +241,36 @@ class DefaultPolicy implements PolicyInterface return $selected; } + + /** + * Assumes that locally aliased (in root package requires) packages take priority over branch-alias ones + */ + protected function pruneToLocalAliases(array $literals) + { + $hasLocalAlias = false; + + foreach ($literals as $literal) { + $package = $literal->getPackage(); + + if ($package instanceof AliasPackage && $package->isRootPackageAlias()) { + $hasLocalAlias = true; + break; + } + } + + if (!$hasLocalAlias) { + return $literals; + } + + $selected = array(); + foreach ($literals as $literal) { + $package = $literal->getPackage(); + + if ($package instanceof AliasPackage && $package->isRootPackageAlias()) { + $selected[] = $literal; + } + } + + return $selected; + } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 1b0b78835..c9e33dd42 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -378,14 +378,16 @@ class Installer foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) { $package->setAlias($alias['alias_normalized']); $package->setPrettyAlias($alias['alias']); - $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $aliasPackage->setRootPackageAlias(true); } foreach ($this->repositoryManager->getLocalRepositories() as $repo) { foreach ($repo->findPackages($alias['package'], $alias['version']) as $package) { $package->setAlias($alias['alias_normalized']); $package->setPrettyAlias($alias['alias']); - $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); $package->getRepository()->removePackage($package); + $aliasPackage->setRootPackageAlias(true); } } } diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index af5887c9e..aee9a65ed 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -27,6 +27,7 @@ class AliasPackage extends BasePackage protected $prettyVersion; protected $dev; protected $aliasOf; + protected $rootPackageAlias = false; protected $requires; protected $conflicts; @@ -146,6 +147,27 @@ class AliasPackage extends BasePackage return $this->devRequires; } + /** + * Stores whether this is an alias created by an aliasing in the requirements of the root package or not + * + * Use by the policy for sorting manually aliased packages first, see #576 + * + * @param Boolean $value + */ + public function setRootPackageAlias($value) + { + return $this->rootPackageAlias = $value; + } + + /** + * @see setRootPackageAlias + * @return Boolean + */ + public function isRootPackageAlias() + { + return $this->rootPackageAlias; + } + /** * {@inheritDoc} */ diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index 4bb7c9e33..8a6c64922 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -18,6 +18,7 @@ use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Literal; use Composer\Package\Link; +use Composer\Package\AliasPackage; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Test\TestCase; @@ -99,6 +100,35 @@ class DefaultPolicyTest extends TestCase $this->assertEquals($expected, $selected); } + public function testSelectLocalReposFirst() + { + $this->repoImportant = new ArrayRepository; + + $this->repo->addPackage($packageA = $this->getPackage('A', 'dev-master')); + $this->repo->addPackage($packageAAlias = new AliasPackage($packageA, '2.1.9999999.9999999-dev', '2.1.x-dev')); + $this->repoImportant->addPackage($packageAImportant = $this->getPackage('A', 'dev-feature-a')); + $this->repoImportant->addPackage($packageAAliasImportant = new AliasPackage($packageAImportant, '2.1.9999999.9999999-dev', '2.1.x-dev')); + $this->repoImportant->addPackage($packageA2Important = $this->getPackage('A', 'dev-master')); + $this->repoImportant->addPackage($packageA2AliasImportant = new AliasPackage($packageA2Important, '2.1.9999999.9999999-dev', '2.1.x-dev')); + $packageAAliasImportant->setRootPackageAlias(true); + + $this->pool->addRepository($this->repoInstalled); + $this->pool->addRepository($this->repoImportant); + $this->pool->addRepository($this->repo); + + $packages = $this->pool->whatProvides('a', new VersionConstraint('=', '2.1.9999999.9999999-dev')); + $literals = array(); + foreach ($packages as $package) { + $literals[] = new Literal($package, true); + } + + $expected = array(new Literal($packageAAliasImportant, true)); + + $selected = $this->policy->selectPreferedPackages($this->pool, array(), $literals); + + $this->assertEquals($expected, $selected); + } + public function testSelectAllProviders() { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); From 15ab5868a339bd523de739929fdb0f89a01a6a88 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 22 Apr 2012 22:11:03 +0200 Subject: [PATCH 015/101] Move composer metafiles back in a composer dir, except autoload.php --- CHANGELOG.md | 2 +- src/Composer/Autoload/AutoloadGenerator.php | 49 ++++---- src/Composer/Compiler.php | 6 +- src/Composer/Factory.php | 18 ++- src/Composer/Installer.php | 2 +- .../Test/Autoload/AutoloadGeneratorTest.php | 106 +++++++++--------- .../Autoload/Fixtures/autoload_classmap.php | 2 +- .../Autoload/Fixtures/autoload_classmap2.php | 2 +- .../Autoload/Fixtures/autoload_classmap3.php | 2 +- .../Autoload/Fixtures/autoload_target_dir.php | 12 +- 10 files changed, 108 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 306521056..fe7e78c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev * Schema: Removed 'recommend' * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint - * Break: .composer/autoload.php and other files in vendor/.composer have been moved to vendor/ + * Break: vendor/.composer/autoload.php has been moved to vendor/autoload.php, other files are now in vendor/composer/ * Added caching of repository metadata (faster startup times & failover if packagist is down) * Added include_path support for legacy projects that are full of require_once statements * Added installation notifications API to allow better statistics on Composer repositories diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index dd6d1ccc3..c94004c20 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -32,7 +32,8 @@ class AutoloadGenerator $filesystem->ensureDirectoryExists($targetDir); $vendorPath = strtr(realpath($installationManager->getVendorPath()), '\\', '/'); $relVendorPath = $filesystem->findShortestPath(getcwd(), $vendorPath, true); - $vendorDirCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); + $vendorPathCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); + $vendorPathToTargetDirCode = $filesystem->findShortestPathCode($vendorPath, realpath($targetDir), true); $appBaseDirCode = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); $appBaseDirCode = str_replace('__DIR__', '$vendorDir', $appBaseDirCode); @@ -42,7 +43,7 @@ class AutoloadGenerator // autoload_namespace.php generated by Composer -\$vendorDir = $vendorDirCode; +\$vendorDir = $vendorPathCode; \$baseDir = $appBaseDirCode; return array( @@ -72,7 +73,7 @@ EOF; // autoload_classmap.php generated by Composer -\$vendorDir = $vendorDirCode; +\$vendorDir = $vendorPathCode; \$baseDir = $appBaseDirCode; return array( @@ -91,12 +92,13 @@ EOF; $targetDirLoader = <<getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorDirCode, $appBaseDirCode)) { + if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorPathCode, $appBaseDirCode)) { file_put_contents($targetDir.'/include_paths.php', $includePathFile); } - file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile, $targetDirLoader)); + file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, true, true, (Boolean) $includePathFile, $targetDirLoader)); copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); // TODO BC feature, add E_DEPRECATED in autoload.php on April 30th, remove after May 30th if ($bcLinks) { - $filesystem->ensureDirectoryExists($targetDir.'/.composer'); - file_put_contents($targetDir.'/.composer/autoload_namespaces.php', "ensureDirectoryExists($vendorPath.'/.composer'); + file_put_contents($vendorPath.'/.composer/autoload_namespaces.php', " $path) { $loader->add($namespace, $path); } @@ -312,7 +315,7 @@ PSR0; if ($useClassMap) { $file .= <<<'CLASSMAP' - $classMap = require __DIR__.'/autoload_classmap.php'; + $classMap = require $composerDir . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index ba915b017..fabd43450 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -81,10 +81,10 @@ class Compiler $this->addFile($phar, $file); } - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/ClassLoader.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload_namespaces.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload_classmap.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/ClassLoader.php')); $this->addComposerBin($phar); // Stubs diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e166754c5..2996c50a5 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -153,13 +153,23 @@ class Factory { // TODO BC feature, remove after May 30th if (file_exists($vendorDir.'/.composer/installed.json')) { - rename($vendorDir.'/.composer/installed.json', $vendorDir.'/installed.json'); + if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); } + rename($vendorDir.'/.composer/installed.json', $vendorDir.'/composer/installed.json'); } if (file_exists($vendorDir.'/.composer/installed_dev.json')) { - rename($vendorDir.'/.composer/installed_dev.json', $vendorDir.'/installed_dev.json'); + if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); } + rename($vendorDir.'/.composer/installed_dev.json', $vendorDir.'/composer/installed_dev.json'); } - $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/installed.json'))); - $rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/installed_dev.json'))); + if (file_exists($vendorDir.'/installed.json')) { + if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); } + rename($vendorDir.'/installed.json', $vendorDir.'/composer/installed.json'); + } + if (file_exists($vendorDir.'/installed_dev.json')) { + if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); } + rename($vendorDir.'/installed_dev.json', $vendorDir.'/composer/installed_dev.json'); + } + $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json'))); + $rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed_dev.json'))); } protected function addPackagistRepository(array $localConfig) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 1b0b78835..49472635e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -174,7 +174,7 @@ class Installer $this->io->write('Generating autoload files'); $generator = new AutoloadGenerator; $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); - $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath(), true); + $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath() . '/composer', true); // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 5cabfd2df..b9974fd9b 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -60,14 +60,14 @@ class AutoloadGeneratorTest extends TestCase protected function tearDown() { if ($this->vendorDir === $this->workingDir) { - if (is_dir($this->workingDir.'/.composer')) { - $this->fs->removeDirectory($this->workingDir.'/.composer'); + if (is_dir($this->workingDir.'/composer')) { + $this->fs->removeDirectory($this->workingDir.'/composer'); } } elseif (is_dir($this->vendorDir)) { $this->fs->removeDirectory($this->vendorDir); } - if (is_dir($this->workingDir.'/.composersrc')) { - $this->fs->removeDirectory($this->workingDir.'/.composersrc'); + if (is_dir($this->workingDir.'/composersrc')) { + $this->fs->removeDirectory($this->workingDir.'/composersrc'); } chdir($this->dir); @@ -78,22 +78,22 @@ class AutoloadGeneratorTest extends TestCase $package = new MemoryPackage('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => array('src/', 'lib/')), - 'classmap' => array('.composersrc/'), + 'classmap' => array('composersrc/'), )); $this->repository->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array())); - if (!is_dir($this->vendorDir.'/.composer')) { - mkdir($this->vendorDir.'/.composer'); + if (!is_dir($this->vendorDir.'/composer')) { + mkdir($this->vendorDir.'/composer'); } $this->createClassFile($this->workingDir); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('main', $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('classmap', $this->vendorDir.'/.composer', 'classmap'); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('main', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } public function testVendorDirSameAsWorkingDir() @@ -103,22 +103,22 @@ class AutoloadGeneratorTest extends TestCase $package = new MemoryPackage('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), - 'classmap' => array('.composersrc/'), + 'classmap' => array('composersrc/'), )); $this->repository->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array())); - if (!is_dir($this->vendorDir.'/.composer')) { - mkdir($this->vendorDir.'/.composer', 0777, true); + if (!is_dir($this->vendorDir.'/composer')) { + mkdir($this->vendorDir.'/composer', 0777, true); } $this->createClassFile($this->vendorDir); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('main3', $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/.composer', 'classmap'); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('main3', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/composer', 'classmap'); } public function testMainPackageAutoloadingAlternativeVendorDir() @@ -126,7 +126,7 @@ class AutoloadGeneratorTest extends TestCase $package = new MemoryPackage('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), - 'classmap' => array('.composersrc/'), + 'classmap' => array('composersrc/'), )); $this->repository->expects($this->once()) @@ -134,11 +134,11 @@ class AutoloadGeneratorTest extends TestCase ->will($this->returnValue(array())); $this->vendorDir .= '/subdir'; - mkdir($this->vendorDir.'/.composer', 0777, true); + mkdir($this->vendorDir.'/composer', 0777, true); $this->createClassFile($this->workingDir); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('main2', $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/.composer', 'classmap'); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('main2', $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/composer', 'classmap'); } public function testMainPackageAutoloadingWithTargetDir() @@ -153,8 +153,8 @@ class AutoloadGeneratorTest extends TestCase ->method('getPackages') ->will($this->returnValue(array())); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertFileEquals(__DIR__.'/Fixtures/autoload_target_dir.php', $this->vendorDir.'/.composer/autoload.php'); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertFileEquals(__DIR__.'/Fixtures/autoload_target_dir.php', $this->vendorDir.'/autoload.php'); } public function testVendorsAutoloading() @@ -171,10 +171,10 @@ class AutoloadGeneratorTest extends TestCase ->method('getPackages') ->will($this->returnValue($packages)); - mkdir($this->vendorDir.'/.composer', 0777, true); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('vendors', $this->vendorDir.'/.composer'); - $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); + mkdir($this->vendorDir.'/composer', 0777, true); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('vendors', $this->vendorDir.'/composer'); + $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); } public function testVendorsClassMapAutoloading() @@ -191,7 +191,7 @@ class AutoloadGeneratorTest extends TestCase ->method('getPackages') ->will($this->returnValue($packages)); - @mkdir($this->vendorDir.'/.composer', 0777, true); + @mkdir($this->vendorDir.'/composer', 0777, true); mkdir($this->vendorDir.'/a/a/src', 0777, true); mkdir($this->vendorDir.'/b/b/src', 0777, true); mkdir($this->vendorDir.'/b/b/lib', 0777, true); @@ -199,17 +199,17 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->vendorDir.'/b/b/src/b.php', 'vendorDir.'/b/b/lib/c.php', 'generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated."); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertEquals( array( 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php', ), - include ($this->vendorDir.'/.composer/autoload_classmap.php') + include ($this->vendorDir.'/composer/autoload_classmap.php') ); - $this->assertAutoloadFiles('classmap4', $this->vendorDir.'/.composer', 'classmap'); + $this->assertAutoloadFiles('classmap4', $this->vendorDir.'/composer', 'classmap'); } public function testClassMapAutoloadingEmptyDirAndExactFile() @@ -228,7 +228,7 @@ class AutoloadGeneratorTest extends TestCase ->method('getPackages') ->will($this->returnValue($packages)); - @mkdir($this->vendorDir.'/.composer', 0777, true); + @mkdir($this->vendorDir.'/composer', 0777, true); mkdir($this->vendorDir.'/a/a/src', 0777, true); mkdir($this->vendorDir.'/b/b', 0777, true); mkdir($this->vendorDir.'/c/c/foo', 0777, true); @@ -236,17 +236,17 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->vendorDir.'/b/b/test.php', 'vendorDir.'/c/c/foo/test.php', 'generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated."); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertEquals( array( 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/test.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/c/c/foo/test.php', ), - include ($this->vendorDir.'/.composer/autoload_classmap.php') + include ($this->vendorDir.'/composer/autoload_classmap.php') ); - $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/.composer', 'classmap'); + $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); } public function testOverrideVendorsAutoloading() @@ -264,9 +264,9 @@ class AutoloadGeneratorTest extends TestCase ->method('getPackages') ->will($this->returnValue($packages)); - mkdir($this->vendorDir.'/.composer', 0777, true); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertAutoloadFiles('override_vendors', $this->vendorDir.'/.composer'); + mkdir($this->vendorDir.'/composer', 0777, true); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer'); + $this->assertAutoloadFiles('override_vendors', $this->vendorDir.'/composer'); } public function testIncludePathFileGeneration() @@ -291,18 +291,18 @@ class AutoloadGeneratorTest extends TestCase ->method("getPackages") ->will($this->returnValue($packages)); - mkdir($this->vendorDir."/.composer", 0777, true); + mkdir($this->vendorDir."/composer", 0777, true); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer"); - $this->assertFileEquals(__DIR__.'/Fixtures/include_paths.php', $this->vendorDir.'/.composer/include_paths.php'); + $this->assertFileEquals(__DIR__.'/Fixtures/include_paths.php', $this->vendorDir.'/composer/include_paths.php'); $this->assertEquals( array( $this->vendorDir."/a/a/lib", $this->vendorDir."/b/b/library", $this->vendorDir."/c/library", ), - require($this->vendorDir."/.composer/include_paths.php") + require($this->vendorDir."/composer/include_paths.php") ); } @@ -320,13 +320,13 @@ class AutoloadGeneratorTest extends TestCase ->method("getPackages") ->will($this->returnValue($packages)); - mkdir($this->vendorDir."/.composer", 0777, true); + mkdir($this->vendorDir."/composer", 0777, true); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer"); $oldIncludePath = get_include_path(); - require($this->vendorDir."/.composer/autoload.php"); + require($this->vendorDir."/autoload.php"); $this->assertEquals( $oldIncludePath.PATH_SEPARATOR.$this->vendorDir."/a/a/lib", @@ -348,20 +348,20 @@ class AutoloadGeneratorTest extends TestCase ->method("getPackages") ->will($this->returnValue($packages)); - mkdir($this->vendorDir."/.composer", 0777, true); + mkdir($this->vendorDir."/composer", 0777, true); - $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); + $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer"); - $this->assertFalse(file_exists($this->vendorDir."/.composer/include_paths.php")); + $this->assertFalse(file_exists($this->vendorDir."/composer/include_paths.php")); } private function createClassFile($basedir) { - if (!is_dir($basedir.'/.composersrc')) { - mkdir($basedir.'/.composersrc', 0777, true); + if (!is_dir($basedir.'/composersrc')) { + mkdir($basedir.'/composersrc', 0777, true); } - file_put_contents($basedir.'/.composersrc/foo.php', ' $baseDir . '/.composersrc/foo.php', + 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php index 2931e0f09..6016af10e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php @@ -6,5 +6,5 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname(dirname($vendorDir)); return array( - 'ClassMapFoo' => $baseDir . '/.composersrc/foo.php', + 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php index 15917ec1a..35f1159c1 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php @@ -6,5 +6,5 @@ $vendorDir = dirname(__DIR__); $baseDir = $vendorDir; return array( - 'ClassMapFoo' => $baseDir . '/.composersrc/foo.php', + 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php index f2acfcc3b..8b90f937c 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_target_dir.php @@ -2,29 +2,31 @@ // autoload.php generated by Composer if (!class_exists('Composer\\Autoload\\ClassLoader', false)) { - require __DIR__.'/ClassLoader.php'; + require __DIR__ . '/composer' . '/ClassLoader.php'; } return call_user_func(function() { $loader = new \Composer\Autoload\ClassLoader(); + $composerDir = __DIR__ . '/composer'; - $map = require __DIR__.'/autoload_namespaces.php'; + $map = require $composerDir . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->add($namespace, $path); } - $classMap = require __DIR__.'/autoload_classmap.php'; + $classMap = require $composerDir . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } spl_autoload_register(function($class) { - $prefixes = array('Main\\Foo', 'Main\\Bar'); + static $dir = dirname(dirname(__DIR__)) . '/'; + static $prefixes = array('Main\\Foo', 'Main\\Bar'); foreach ($prefixes as $prefix) { if (0 !== strpos($class, $prefix)) { continue; } - $path = dirname(dirname(__DIR__)) . '/' . implode('/', array_slice(explode('\\', $class), 2)).'.php'; + $path = $dir . implode('/', array_slice(explode('\\', $class), 2)).'.php'; if (!stream_resolve_include_path($path)) { return false; } From ebf78135c959e0f2fb82397d1561bb8bf9552b31 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 23 Apr 2012 09:24:59 +0200 Subject: [PATCH 016/101] Rename method --- src/Composer/DependencyResolver/DefaultPolicy.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 9b77e8974..ac3d38995 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -70,7 +70,7 @@ class DefaultPolicy implements PolicyInterface $literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals); - $literals = $this->pruneToLocalAliases($literals); + $literals = $this->pruneRemoteAliases($literals); } $selected = call_user_func_array('array_merge', $packages); @@ -244,8 +244,10 @@ class DefaultPolicy implements PolicyInterface /** * Assumes that locally aliased (in root package requires) packages take priority over branch-alias ones + * + * If no package is a local alias, nothing happens */ - protected function pruneToLocalAliases(array $literals) + protected function pruneRemoteAliases(array $literals) { $hasLocalAlias = false; From 6a50dbd46dee201e2952c8feda25acf28784721e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 23 Apr 2012 10:14:02 +0200 Subject: [PATCH 017/101] Error out on wrong bin config --- src/Composer/Package/Loader/ArrayLoader.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index ae5678375..475fd122c 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -58,7 +58,10 @@ class ArrayLoader $package->setExtra($config['extra']); } - if (isset($config['bin']) && is_array($config['bin'])) { + if (isset($config['bin'])) { + if (!is_array($config['bin'])) { + 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, '/'); } From aff7745231fc2fd83238edd10ef949da054d0bd7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 23 Apr 2012 10:56:46 +0200 Subject: [PATCH 018/101] Fix target-dir autoloader --- src/Composer/Autoload/AutoloadGenerator.php | 6 +++--- .../Composer/Test/Autoload/Fixtures/autoload_target_dir.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c94004c20..3d8c7b752 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -88,12 +88,12 @@ EOF; $prefixes = implode(', ', array_map(function ($prefix) { return var_export($prefix, true); }, array_keys($mainAutoload['psr-0']))); - $baseDirFromTargetDirCode = $filesystem->findShortestPathCode(realpath($targetDir), getcwd(), true); + $baseDirFromVendorDirCode = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); $targetDirLoader = << Date: Tue, 24 Apr 2012 10:49:49 +0200 Subject: [PATCH 019/101] Make sure aliased packages created by the locker are available in the pool --- src/Composer/Installer.php | 7 ++++++- src/Composer/Package/Locker.php | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 49472635e..583d27498 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -186,6 +186,11 @@ class Installer protected function doInstall($localRepo, $installedRepo, $aliases, $devMode = false) { + // initialize locker to create aliased packages + if (!$this->update && $this->locker->isLocked($devMode)) { + $lockedPackages = $this->locker->getLockedPackages($devMode); + } + // creating repository pool $pool = new Pool; $pool->addRepository($installedRepo); @@ -214,7 +219,7 @@ class Installer $this->io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); } - foreach ($this->locker->getLockedPackages($devMode) as $package) { + foreach ($lockedPackages as $package) { $version = $package->getVersion(); foreach ($aliases as $alias) { if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index fc448c360..e8037b464 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -95,7 +95,9 @@ class Locker if (!$package) { $package = $this->repositoryManager->findPackage($info['package'], $info['version']); if ($package && !empty($info['alias'])) { - $package = new AliasPackage($package, $info['alias'], $info['alias']); + $alias = new AliasPackage($package, $info['alias'], $info['alias']); + $package->getRepository()->addPackage($alias); + $package = $alias; } } From 903facd64fee3941c4abc3e945982395cfb166b4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 24 Apr 2012 11:16:25 +0200 Subject: [PATCH 020/101] Make sure the anti-aliased package contains the alias info from the correct alias --- .../Installer/InstallationManager.php | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index f2fd405c8..e5080e0ef 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -127,11 +127,7 @@ class InstallationManager */ public function install(RepositoryInterface $repo, InstallOperation $operation) { - $package = $operation->getPackage(); - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - $package->setInstalledAsAlias(true); - } + $package = $this->antiAlias($operation->getPackage()); $installer = $this->getInstaller($package->getType()); $installer->install($repo, $package); $this->notifyInstall($package); @@ -145,15 +141,8 @@ class InstallationManager */ public function update(RepositoryInterface $repo, UpdateOperation $operation) { - $initial = $operation->getInitialPackage(); - if ($initial instanceof AliasPackage) { - $initial = $initial->getAliasOf(); - } - $target = $operation->getTargetPackage(); - if ($target instanceof AliasPackage) { - $target = $target->getAliasOf(); - $target->setInstalledAsAlias(true); - } + $initial = $this->antiAlias($operation->getInitialPackage()); + $target = $this->antiAlias($operation->getTargetPackage()); $initialType = $initial->getType(); $targetType = $target->getType(); @@ -176,10 +165,7 @@ class InstallationManager */ public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) { - $package = $operation->getPackage(); - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } + $package = $this->antiAlias($operation->getPackage()); $installer = $this->getInstaller($package->getType()); $installer->uninstall($repo, $package); } @@ -217,4 +203,17 @@ class InstallationManager $package->getRepository()->notifyInstall($package); } } + + private function antiAlias(PackageInterface $package) + { + if ($package instanceof AliasPackage) { + $alias = $package; + $package = $package->getAliasOf(); + $package->setInstalledAsAlias(true); + $package->setAlias($alias->getVersion()); + $package->setPrettyAlias($alias->getPrettyVersion()); + } + + return $package; + } } From 66a202083e2548180593b673a8bf4e50bc42267d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 26 Apr 2012 14:54:34 +0200 Subject: [PATCH 021/101] Add IOInterface::isDecorated() --- src/Composer/IO/ConsoleIO.php | 8 ++++++++ src/Composer/IO/IOInterface.php | 7 +++++++ src/Composer/IO/NullIO.php | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index d11ed44d1..95aed6a29 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -55,6 +55,14 @@ class ConsoleIO implements IOInterface return $this->input->isInteractive(); } + /** + * {@inheritDoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + /** * {@inheritDoc} */ diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index 5b540892a..583800424 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -33,6 +33,13 @@ interface IOInterface */ function isVerbose(); + /** + * Is this output decorated? + * + * @return Boolean + */ + function isDecorated(); + /** * Writes a message to the output. * diff --git a/src/Composer/IO/NullIO.php b/src/Composer/IO/NullIO.php index b57e42087..78688f6d5 100644 --- a/src/Composer/IO/NullIO.php +++ b/src/Composer/IO/NullIO.php @@ -35,6 +35,14 @@ class NullIO implements IOInterface return false; } + /** + * {@inheritDoc} + */ + public function isDecorated() + { + return false; + } + /** * {@inheritDoc} */ From 70ad8274cd6827c545087b07a3f3073ce721c659 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 26 Apr 2012 15:47:22 +0200 Subject: [PATCH 022/101] Work around poorly configured php --- 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 9bdd9f05d..f7e021e3e 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -70,7 +70,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository array( 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', - 'content' => http_build_query($params), + 'content' => http_build_query($params, '', '&'), 'timeout' => 3, ) ); From 59a4156f2ced2cad327cfde995e7c85764884d77 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 26 Apr 2012 15:49:09 +0200 Subject: [PATCH 023/101] Update lock --- composer.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.lock b/composer.lock index 977007a41..2a5f4ee4f 100644 --- a/composer.lock +++ b/composer.lock @@ -12,19 +12,19 @@ { "package": "symfony/console", "version": "dev-master", - "source-reference": "8e3c42aa976f18a9bfcb0694553e5f99def3309c", + "source-reference": "8c69d3a3e26885cad132a38ab0d7f287fb975296", "alias": "2.1.9999999.9999999-dev" }, { "package": "symfony/finder", "version": "dev-master", - "source-reference": "57ec7198a70e6c40e450ba66cc2f8ecab98746c8", + "source-reference": "5b8658e52eaf424fe943a5857e2404ce95c3b90e", "alias": "2.1.9999999.9999999-dev" }, { "package": "symfony/process", "version": "dev-master", - "source-reference": "2e4da8c8076744bafed97451bb1574c96cda0e68", + "source-reference": "3f95a0126588f00d70e745c1ef96631fb10c3a8e", "alias": "2.1.9999999.9999999-dev" } ], From f5196360944e63c41fcd43b9c3e1200778d70354 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 17:19:07 +0200 Subject: [PATCH 024/101] [docs] Document the notify-on-install config option --- doc/04-schema.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index 2b7aef879..cc91d9905 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -368,6 +368,9 @@ The following options are supported: * **process-timeout:** Defaults to `300`. The duration processes like git clones can run before Composer assumes they died out. You may need to make this higher if you have a slow connection or huge vendors. +* **notify-on-install:** Defaults to `true`. Composer allows repositories to + define a notification URL, so that they get notified whenever a package is + installed. This option allows you to disable that behaviour. Example: From f6259263ad9c7118250b16630fba059f3adcf383 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 17:19:39 +0200 Subject: [PATCH 025/101] [docs] Update composer repository specification --- doc/05-repositories.md | 81 +++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index ba1272d21..cc633f439 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -54,15 +54,24 @@ want to learn why. ### Composer The main repository type is the `composer` repository. It uses a single -`packages.json` file that contains all of the package metadata. The JSON -format is as follows: +`packages.json` file that contains all of the package metadata. + +This is also the repository type that packagist uses. To reference a +`composer` repository, just supply the path before the `packages.json` file. +In case of packagist, that file is located at `/packages.json`, so the URL of +the repository would be `packagist.org`. For `example.org/packages.json` the +repository URL would be `example.org`. + +#### packages + +The only required field is `packages`. The JSON structure is as follows: { - "vendor/packageName": { - "name": "vendor/packageName", - "description": "Package description", - "versions": { + "packages": { + "vendor/packageName": { "master-dev": { @composer.json }, + "1.0.x-dev": { @composer.json }, + "0.0.1": { @composer.json }, "1.0.0": { @composer.json } } } @@ -88,12 +97,60 @@ Here is a minimal package definition: It may include any of the other fields specified in the [schema](04-schema.md). -The `composer` repository is also what packagist uses. To reference a -`composer` repository, just supply the path before the `packages.json` file. -In case of packagist, that file is located at `/packages.json`, so the URL of -the repository would be `http://packagist.org`. For -`http://example.org/packages.json` the repository URL would be -`http://example.org`. +> **Note:** Optionally packages can contain a `version_normalized` field, with +> the pre-parsed version. This improves performance for large repositories such +> as packagist, as the `version` field no longer has to be converted to the +> internal representation. + +#### notify + +The `notify` field allows you to specify an URL template for a URL that will +be called every time a user installs a package. + +An example value: + + { + "notify": "/downloads/%package%" + } + +For `example.org/packages.json` containing a `monolog/monolog` package, this +would send a `POST` request to `example.org/downloads/monolog/monolog` with +following parameters: + +* **version:** The version of the package. +* **version_normalized:** The normalized internal representation of the + version. + +This field is optional. + +#### includes + +For large repositories it is possible to split the `packages.json` into +multiple files. The `includes` field from the root `packages.json` allows you +to reference these additional files. + +An example: + + { + "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. Do note that `includes` are not resolved recursively, they can +only be specified in the root `packages.json`. + +This field is optional. You probably don't need it for your own custom +repository. ### VCS From b14aac84aeb75629d35d17e589929b57a8912903 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 17:24:31 +0200 Subject: [PATCH 026/101] [docs] Document COMPOSER_HOME --- doc/03-cli.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 4648aa42a..ecded9528 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -199,11 +199,6 @@ directory other than `vendor`. By setting this option you can change the `bin` ([Vendor Bins](articles/vendor-bins.md)) directory to something other than `vendor/bin`. -### COMPOSER_PROCESS_TIMEOUT - -This env var controls the time composer waits for commands (such as git -commands) to finish executing. The default value is 60 seconds. - ### http_proxy or HTTP_PROXY If you are using composer from behind an HTTP proxy, you can use the standard @@ -215,4 +210,18 @@ some tools like git or curl will only use the lower-cased `http_proxy` version. Alternatively you can also define the git proxy using `git config --global http.proxy `. +### COMPOSER_HOME + +The `COMPOSER_HOME` var allows you to change the composer home directory. This +is a global (per-user on the machine) hidden directory that caches repository +data. + +By default it points to `$HOME/.composer` on *nix and `$APPDATA/Composer` on +Windows. + +### COMPOSER_PROCESS_TIMEOUT + +This env var controls the time composer waits for commands (such as git +commands) to finish executing. The default value is 60 seconds. + ← [Libraries](02-libraries.md) | [Schema](04-schema.md) → From 7b882479a7358fdb9627a8afad2fd2a19095a256 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 18:40:00 +0200 Subject: [PATCH 027/101] [docs] Aliases: branch-alias and inline aliasing --- doc/02-libraries.md | 8 ++++ doc/articles/aliases.md | 82 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 doc/articles/aliases.md diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 990246537..81d665fda 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -76,6 +76,14 @@ Here are some examples of version branch names: > **Note:** When you install a dev version, it will install it from source. See [Repositories](05-repositories.md) for more information. +### Aliases + +It is possible alias branch names to versions. For example, you could alias +`dev-master` to `1.0-dev`, which would allow you to require `1.0-dev` in all +the packages. + +See [Aliases](articles/aliases.md) for more information. + ## Lock file For your library you may commit the `composer.lock` file if you want to. This diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md new file mode 100644 index 000000000..bbde23b94 --- /dev/null +++ b/doc/articles/aliases.md @@ -0,0 +1,82 @@ + +# Aliases + +## 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 +will get a `dev-master` version. For your `bugfix` branch, you will get a +`dev-bugfix` version. + +If your `master` branch is used to tag releases of the `1.0` development line, +i.e. `1.0.1`, `1.0.2`, `1.0.3`, etc., any package depending on it will +probably require version `1.0.*`. + +If anyone wants to require the latest `dev-master`, they have a problem: Other +packages may require `1.0.*`, so requiring that dev version will lead to +conflicts, since `dev-master` does not match the `1.0.*` constraint. + +Enter aliases. + +## Branch alias + +The `dev-master` branch is one in your main VCS repo. It is rather common that +someone will want the latest master dev version. Thus, Composer allows you to +alias your `dev-master` branch to a `1.0-dev` version. It is done by +specifying a `branch-alias` field under `extra` in `composer.json`: + + { + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } + } + +The branch version must begin with `dev-` (non-comparable version), the alias +must be a comparable dev version. 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, you can now require `1.0.*` and it will happily install +`dev-master` for you. + +## Require inline alias + +Branch aliases are great for aliasing main development lines. But in order to +use them you need to have control over the source repository, and you need to +commit changes to version control. + +This is not really fun when you just want to try a bugfix of some library that +is a dependency of your local project. + +For this reason, you can alias packages in your `require` field. Let's say you +found a bug in the `monolog/monolog` package. You cloned Monolog on GitHub and +fixed the issue in a branch named `bugfix`. Now you want to install that +version of monolog in your local project. + +Just add this to your project's root `composer.json`: + + { + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/you/monolog" + } + ], + "require": { + "monolog/monolog": "dev-bugfix as 1.0-dev" + } + } + +That will fetch the `dev-bugfix` version of `monolog/monolog` from your GitHub +and alias it to `1.0-dev`. + +> **Note:** If a package with inline aliases is required, the alias (right of +> the `as`) is used as the version constraint. The part left of the `as` is +> discarded. As a consequence, if A requires B and B requires `monolog/monolog` +> version `dev-bugfix as 1.0-dev`, installing A will make B require `1.0-dev`, +> which may exist as a branch alias or an actual `1.0` branch. If it does not, +> it must be re-inline-aliased in A's `composer.json`. From 1d3fd25cf2b9557a76622fca0e700fe72d0dece0 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 18:54:45 +0200 Subject: [PATCH 028/101] [docs] Improve description of COMPOSER_HOME --- doc/03-cli.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index ecded9528..8cceb71f6 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -213,11 +213,12 @@ Alternatively you can also define the git proxy using ### COMPOSER_HOME The `COMPOSER_HOME` var allows you to change the composer home directory. This -is a global (per-user on the machine) hidden directory that caches repository -data. +is a hidden, global (per-user on the machine) directory that is shared between +all projects. -By default it points to `$HOME/.composer` on *nix and `$APPDATA/Composer` on -Windows. +By default it points to `/home//.composer` on *nix, +`/Users//.composer` on OSX and +`C:\Users\\AppData\Roaming\Composer` on Windows. ### COMPOSER_PROCESS_TIMEOUT From bca2ab7836cf1024d7104b6f674b1a5d9135f8ea Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 18:59:09 +0200 Subject: [PATCH 029/101] [docs] Correct default process timeout --- 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 8cceb71f6..94d5b001a 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -223,6 +223,6 @@ By default it points to `/home//.composer` on *nix, ### COMPOSER_PROCESS_TIMEOUT This env var controls the time composer waits for commands (such as git -commands) to finish executing. The default value is 60 seconds. +commands) to finish executing. The default value is 300 seconds (5 minutes). ← [Libraries](02-libraries.md) | [Schema](04-schema.md) → From 21b39f8e87b4082b392b375bae633ef704b80a77 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 19:00:55 +0200 Subject: [PATCH 030/101] [docs] Clarify that there is no cross-repo leakage with notify --- doc/04-schema.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index cc91d9905..7c0b03acc 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -369,8 +369,8 @@ The following options are supported: can run before Composer assumes they died out. You may need to make this higher if you have a slow connection or huge vendors. * **notify-on-install:** Defaults to `true`. Composer allows repositories to - define a notification URL, so that they get notified whenever a package is - installed. This option allows you to disable that behaviour. + 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. Example: From 8dd2a72e8df79d846c3db559c6e3be542ba0c8e3 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 19:02:27 +0200 Subject: [PATCH 031/101] [docs] Remove mentions of version_normalized People will get it wrong so it would possibly cause more harm than good --- doc/05-repositories.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index cc633f439..48a253128 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -97,11 +97,6 @@ Here is a minimal package definition: It may include any of the other fields specified in the [schema](04-schema.md). -> **Note:** Optionally packages can contain a `version_normalized` field, with -> the pre-parsed version. This improves performance for large repositories such -> as packagist, as the `version` field no longer has to be converted to the -> internal representation. - #### notify The `notify` field allows you to specify an URL template for a URL that will From 3b407710897905c6107c03d44099d0c120677213 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 19:04:06 +0200 Subject: [PATCH 032/101] [docs] Remove false information about recursive include fields --- doc/05-repositories.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 48a253128..894e35230 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -121,8 +121,8 @@ This field is optional. #### includes For large repositories it is possible to split the `packages.json` into -multiple files. The `includes` field from the root `packages.json` allows you -to reference these additional files. +multiple files. The `includes` field allows you to reference these additional +files. An example: @@ -141,8 +141,7 @@ An example: } The SHA-1 sum of the file allows it to be cached and only re-requested if the -hash changed. Do note that `includes` are not resolved recursively, they can -only be specified in the root `packages.json`. +hash changed. This field is optional. You probably don't need it for your own custom repository. From 6a1843da38dbc8e2120549dbe07a9403b4853f84 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Thu, 26 Apr 2012 19:21:31 +0200 Subject: [PATCH 033/101] [docs] Change alias article according to comments 1.0.x-dev alias is safer than 1.0-dev. require-dev is also supported. Clarify that inline aliases are for matching constraints of deps. Tell users to avoid inline aliasing if possible. --- doc/articles/aliases.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index bbde23b94..38e0e65ee 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -24,13 +24,13 @@ Enter aliases. The `dev-master` branch is one in your main VCS repo. It is rather common that someone will want the latest master dev version. Thus, Composer allows you to -alias your `dev-master` branch to a `1.0-dev` version. It is done by +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-dev" + "dev-master": "1.0.x-dev" } } } @@ -52,10 +52,13 @@ commit changes to version control. This is not really fun when you just want to try a bugfix of some library that is a dependency of your local project. -For this reason, you can alias packages in your `require` field. Let's say you -found a bug in the `monolog/monolog` package. You cloned Monolog on GitHub and -fixed the issue in a branch named `bugfix`. Now you want to install that -version of monolog in your local project. +For this reason, you can alias packages in your `require` and `require-dev` +fields. Let's say you found a bug in the `monolog/monolog` package. You cloned +Monolog on GitHub and fixed the issue in a branch named `bugfix`. Now you want +to install that version of monolog in your local project. + +You are using `symfony/monolog-bundle` which requires `monolog/monolog` version +`1.*`. So you need your `dev-bugfix` to match that constraint. Just add this to your project's root `composer.json`: @@ -67,16 +70,21 @@ Just add this to your project's root `composer.json`: } ], "require": { - "monolog/monolog": "dev-bugfix as 1.0-dev" + "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-dev`. +and alias it to `1.0.x-dev`. > **Note:** If a package with inline aliases is required, the alias (right of > the `as`) is used as the version constraint. The part left of the `as` is > discarded. As a consequence, if A requires B and B requires `monolog/monolog` -> version `dev-bugfix as 1.0-dev`, installing A will make B require `1.0-dev`, -> which may exist as a branch alias or an actual `1.0` branch. If it does not, -> it must be re-inline-aliased in A's `composer.json`. +> version `dev-bugfix as 1.0.x-dev`, installing A will make B require +> `1.0.x-dev`, which may exist as a branch alias or an actual `1.0` branch. If +> it does not, it must be re-inline-aliased in A's `composer.json`. + +> **Note:** Inline aliasing should be avoided, especially for published +> packages. If you found a bug, try and get your fix merged upstream. This +> helps to avoid issues for users of your package. From e7ba0c38a8c2c320c38d255b266075cd70c262f0 Mon Sep 17 00:00:00 2001 From: Danny Berger Date: Thu, 26 Apr 2012 21:38:12 -0400 Subject: [PATCH 034/101] Add default http(s) ports to proxy configuration if they are missing. --- src/Composer/Util/StreamContextFactory.php | 6 ++- .../Test/Util/StreamContextFactoryTest.php | 37 ++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 6541d811b..a859cac13 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -37,7 +37,11 @@ final class StreamContextFactory $proxy = isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']; // http(s):// is not supported in proxy - $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxy); + if ('http://' == substr($proxy, 0, 7)) { + $proxy = 'tcp://' . rtrim(substr($proxy, 7), '/') . (parse_url($proxy, PHP_URL_PORT) ? '' : ':80'); + } else if ('https://' == substr($proxy, 0, 8)) { + $proxy = 'ssl://' . rtrim(substr($proxy, 8), '/') . (parse_url($proxy, PHP_URL_PORT) ? '' : ':443'); + } if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 8429d078b..237d4aaa9 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -57,7 +57,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase public function testHttpProxy() { - $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:port/'; + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:3128/'; $_SERVER['HTTP_PROXY'] = 'http://proxyserver/'; $context = StreamContextFactory::getContext(array('http' => array('method' => 'GET'))); @@ -66,22 +66,39 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase $this->assertSame('http://proxyserver/', $_SERVER['HTTP_PROXY']); $this->assertEquals(array('http' => array( - 'proxy' => 'tcp://username:password@proxyserver.net:port/', + 'proxy' => 'tcp://username:password@proxyserver.net:3128', 'request_fulluri' => true, 'method' => 'GET', )), $options); } - public function testSSLProxy() + public function testHttpProxyWithoutPort() { - $_SERVER['http_proxy'] = 'https://proxyserver/'; + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; + + $context = StreamContextFactory::getContext(array('http' => array('method' => 'GET'))); + $options = stream_context_get_options($context); + + $this->assertEquals(array('http' => array( + 'proxy' => 'tcp://username:password@proxyserver.net:80', + 'request_fulluri' => true, + 'method' => 'GET', + )), $options); + } + + /** + * @dataProvider dataSSLProxy + */ + public function testSSLProxy($expected, $proxy) + { + $_SERVER['http_proxy'] = $proxy; if (extension_loaded('openssl')) { $context = StreamContextFactory::getContext(); $options = stream_context_get_options($context); - $this->assertSame(array('http' => array( - 'proxy' => 'ssl://proxyserver/', + $this->assertEquals(array('http' => array( + 'proxy' => $expected, 'request_fulluri' => true, )), $options); } else { @@ -93,4 +110,12 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase } } } + + public function dataSSLProxy() + { + return array( + array('ssl://proxyserver:443', 'https://proxyserver/'), + array('ssl://proxyserver:8443', 'https://proxyserver:8443'), + ); + } } From 939c745a9a3e65d6606aaecba56b4e73d2bd8088 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 10:01:01 +0200 Subject: [PATCH 035/101] Improvements to autoload docs --- doc/04-schema.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 7c0b03acc..b9552061e 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -215,21 +215,26 @@ Example: Autoload mapping for a PHP autoloader. Currently [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) -autoloading and classmap generation are supported. +autoloading and classmap generation are supported. PSR-0 is the recommended way though +since it offers greater flexibility (no need to regenerate the autoloader when you add +classes). Under the `psr-0` key you define a mapping from namespaces to paths, relative to the -package root. +package root. Note that this also supports the PEAR-style convention. Example: { "autoload": { - "psr-0": { "Monolog": "src/" } + "psr-0": { + "Monolog": "src/", + "Vendor\\Namespace": "src/", + "Pear_Style": "src/" + } } } -Optional, but it is highly recommended that you follow PSR-0 and use this. -If you need to search for a same namespace prefix in multiple directories, +If you need to search for a same prefix in multiple directories, you can specify them as an array as such: { @@ -238,15 +243,24 @@ you can specify them as an array as such: } } +If you want to have a fallback directory where any namespace can be, you can +use an empty prefix like: + + { + "autoload": { + "psr-0": { "": "src/" } + } + } + You can use the classmap generation support to define autoloading for all libraries -that do not follow PSR-0. To configure this you specify all directories +that do not follow PSR-0. To configure this you specify all directories or files to search for classes. Example: { "autoload: { - "classmap": ["src/", "lib/"] + "classmap": ["src/", "lib/", "Something.php"] } } From d489d2aa3ce66b66ece33522c7216be81b68eafb Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Tue, 24 Apr 2012 14:39:30 +0200 Subject: [PATCH 036/101] Store cached git repositories in user HOME to avoid file permission issues --- 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 73f9e1aed..4fe4fafb4 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -41,7 +41,7 @@ class GitDriver extends VcsDriver if (static::isLocalUrl($this->url)) { $this->repoDir = str_replace('file://', '', $this->url); } else { - $this->repoDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; + $this->repoDir = getenv('HOME') . '/.composer/cache.git/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; // update the repo if it is a valid git repository if (is_dir($this->repoDir) && 0 === $this->process->execute('git remote', $output, $this->repoDir)) { From 017ebabcb5e9470c044bdbd3514c7bf93bc79b24 Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Tue, 24 Apr 2012 16:56:03 +0200 Subject: [PATCH 037/101] Pass composer configuration to VcsDriver --- src/Composer/Repository/Vcs/VcsDriver.php | 7 +++++-- src/Composer/Repository/VcsRepository.php | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 99c705c50..f8d9893b9 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -13,6 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Downloader\TransportException; +use Composer\Config; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; @@ -26,6 +27,7 @@ abstract class VcsDriver implements VcsDriverInterface { protected $url; protected $io; + protected $config; protected $process; protected $remoteFilesystem; @@ -34,13 +36,15 @@ abstract class VcsDriver implements VcsDriverInterface * * @param string $url The URL * @param IOInterface $io The IO instance + * @param Config $config The composer configuration * @param ProcessExecutor $process Process instance, injectable for mocking * @param callable $remoteFilesystem Remote Filesystem, injectable for mocking */ - public function __construct($url, IOInterface $io, ProcessExecutor $process = null, $remoteFilesystem = null) + public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null, $remoteFilesystem = null) { $this->url = $url; $this->io = $io; + $this->config = $config; $this->process = $process ?: new ProcessExecutor; $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); } @@ -58,7 +62,6 @@ abstract class VcsDriver implements VcsDriverInterface return false; } - /** * Get the https or http protocol depending on SSL support. * diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index b70e7c09d..ecc435582 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -30,6 +30,7 @@ class VcsRepository extends ArrayRepository protected $packageName; protected $verbose; protected $io; + protected $config; protected $versionParser; protected $type; @@ -48,20 +49,21 @@ class VcsRepository extends ArrayRepository $this->io = $io; $this->type = isset($repoConfig['type']) ? $repoConfig['type'] : 'vcs'; $this->verbose = $io->isVerbose(); + $this->config = $config; } public function getDriver() { if (isset($this->drivers[$this->type])) { $class = $this->drivers[$this->type]; - $driver = new $class($this->url, $this->io); + $driver = new $class($this->url, $this->io, $this->config); $driver->initialize(); return $driver; } foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->url)) { - $driver = new $driver($this->url, $this->io); + $driver = new $driver($this->url, $this->io, $this->config); $driver->initialize(); return $driver; } @@ -69,7 +71,7 @@ class VcsRepository extends ArrayRepository foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->url, true)) { - $driver = new $driver($this->url, $this->io); + $driver = new $driver($this->url, $this->io, $this->config); $driver->initialize(); return $driver; } From 949728f81274cf2d828b52c1342f90b959c70ed2 Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Tue, 24 Apr 2012 16:58:29 +0200 Subject: [PATCH 038/101] Remove redundant __contruct from VcsDriver implementations and move init code to initialize method --- .../Repository/Vcs/GitBitbucketDriver.php | 12 +++------ src/Composer/Repository/Vcs/GitDriver.php | 5 ---- src/Composer/Repository/Vcs/GitHubDriver.php | 21 +++------------- .../Repository/Vcs/HgBitbucketDriver.php | 12 +++------ src/Composer/Repository/Vcs/HgDriver.php | 9 ++----- src/Composer/Repository/Vcs/SvnDriver.php | 25 ++++++------------- src/Composer/Repository/Vcs/VcsDriver.php | 2 +- 7 files changed, 20 insertions(+), 66 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index c0132cd10..b5cb8960d 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -27,20 +27,14 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, IOInterface $io) - { - preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); - $this->owner = $match[1]; - $this->repository = $match[2]; - - parent::__construct($url, $io); - } - /** * {@inheritDoc} */ public function initialize() { + preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match); + $this->owner = $match[1]; + $this->repository = $match[2]; } /** diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 4fe4fafb4..dd927fd1c 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -28,11 +28,6 @@ class GitDriver extends VcsDriver protected $repoDir; protected $infoCache = array(); - public function __construct($url, IOInterface $io, ProcessExecutor $process = null) - { - parent::__construct($url, $io, $process); - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 42633aafe..61a0589ac 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -38,28 +38,15 @@ class GitHubDriver extends VcsDriver */ protected $gitDriver; - /** - * Constructor - * - * @param string $url - * @param IOInterface $io - * @param ProcessExecutor $process - * @param RemoteFilesystem $remoteFilesystem - */ - public function __construct($url, IOInterface $io, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) - { - preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); - $this->owner = $match[1]; - $this->repository = $match[2]; - - parent::__construct($url, $io, $process, $remoteFilesystem); - } - /** * {@inheritDoc} */ public function initialize() { + preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match); + $this->owner = $match[1]; + $this->repository = $match[2]; + $this->fetchRootIdentifier(); } diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 993ece82d..43fff9aba 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -27,20 +27,14 @@ class HgBitbucketDriver extends VcsDriver protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, IOInterface $io) - { - preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); - $this->owner = $match[1]; - $this->repository = $match[2]; - - parent::__construct($url, $io); - } - /** * {@inheritDoc} */ public function initialize() { + preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match); + $this->owner = $match[1]; + $this->repository = $match[2]; } /** diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 833669cf9..17310d630 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -26,18 +26,13 @@ class HgDriver extends VcsDriver protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, IOInterface $io, ProcessExecutor $process = null) - { - $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - - parent::__construct($url, $io, $process); - } - /** * {@inheritDoc} */ public function initialize() { + $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; + $url = escapeshellarg($this->url); $tmpDir = escapeshellarg($this->tmpDir); if (is_dir($this->tmpDir)) { diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 6f13296ce..f5c3e04cd 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -35,24 +35,6 @@ class SvnDriver extends VcsDriver */ protected $util; - /** - * @param string $url - * @param IOInterface $io - * @param ProcessExecutor $process - * - * @return $this - */ - public function __construct($url, IOInterface $io, ProcessExecutor $process = null) - { - $url = self::normalizeUrl($url); - parent::__construct($this->baseUrl = rtrim($url, '/'), $io, $process); - - if (false !== ($pos = strrpos($url, '/trunk'))) { - $this->baseUrl = substr($url, 0, $pos); - } - $this->util = new SvnUtil($this->baseUrl, $io, $this->process); - } - /** * Execute an SVN command and try to fix up the process with credentials * if necessary. @@ -78,6 +60,13 @@ class SvnDriver extends VcsDriver */ public function initialize() { + $this->url = rtrim(self::normalizeUrl($this->url), '/'); + + if (false !== ($pos = strrpos($url, '/trunk'))) { + $this->baseUrl = substr($url, 0, $pos); + } + $this->util = new SvnUtil($this->baseUrl, $this->io, $this->process); + $this->getBranches(); $this->getTags(); } diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index f8d9893b9..d13b4b4a1 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -40,7 +40,7 @@ abstract class VcsDriver implements VcsDriverInterface * @param ProcessExecutor $process Process instance, injectable for mocking * @param callable $remoteFilesystem Remote Filesystem, injectable for mocking */ - public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null, $remoteFilesystem = null) + final public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null, $remoteFilesystem = null) { $this->url = $url; $this->io = $io; From da823f5f19c055e13ad001e01a4ee956e55398ce Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Tue, 24 Apr 2012 17:03:15 +0200 Subject: [PATCH 039/101] Store vcs repositories in composer home to prevent permission issues --- src/Composer/Repository/Vcs/GitDriver.php | 2 +- src/Composer/Repository/Vcs/HgDriver.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index dd927fd1c..94ccb61ce 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -36,7 +36,7 @@ class GitDriver extends VcsDriver if (static::isLocalUrl($this->url)) { $this->repoDir = str_replace('file://', '', $this->url); } else { - $this->repoDir = getenv('HOME') . '/.composer/cache.git/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; + $this->repoDir = $this->config->get('home') . '/cache.git/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; // update the repo if it is a valid git repository if (is_dir($this->repoDir) && 0 === $this->process->execute('git remote', $output, $this->repoDir)) { diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 17310d630..aa0ee6bfd 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -31,14 +31,14 @@ class HgDriver extends VcsDriver */ public function initialize() { - $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; + $this->tmpDir = $this->config->get('home') . '/cache.hg/' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; $url = escapeshellarg($this->url); $tmpDir = escapeshellarg($this->tmpDir); if (is_dir($this->tmpDir)) { $this->process->execute(sprintf('cd %s && hg pull -u', $tmpDir), $output); } else { - $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(sys_get_temp_dir()), $url, $tmpDir), $output); + $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), $url, $tmpDir), $output); } $this->getTags(); From cc6e1397b2230a553a69dde6f2d353918c5870cc Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Tue, 24 Apr 2012 17:15:31 +0200 Subject: [PATCH 040/101] Fix var scope --- src/Composer/Repository/Vcs/SvnDriver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f5c3e04cd..388b21f34 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -62,8 +62,8 @@ class SvnDriver extends VcsDriver { $this->url = rtrim(self::normalizeUrl($this->url), '/'); - if (false !== ($pos = strrpos($url, '/trunk'))) { - $this->baseUrl = substr($url, 0, $pos); + if (false !== ($pos = strrpos($this->url, '/trunk'))) { + $this->baseUrl = substr($this->url, 0, $pos); } $this->util = new SvnUtil($this->baseUrl, $this->io, $this->process); From d2004810370e89840c608c4b0e86896b81fae7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 25 Apr 2012 09:49:05 +0300 Subject: [PATCH 041/101] Fix tmp dir for HgDriver --- src/Composer/Repository/Vcs/HgDriver.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index aa0ee6bfd..08f34be9a 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -34,11 +34,10 @@ class HgDriver extends VcsDriver $this->tmpDir = $this->config->get('home') . '/cache.hg/' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; $url = escapeshellarg($this->url); - $tmpDir = escapeshellarg($this->tmpDir); if (is_dir($this->tmpDir)) { - $this->process->execute(sprintf('cd %s && hg pull -u', $tmpDir), $output); + $this->process->execute(sprintf('cd %s && hg pull -u', escapeshellarg($this->tmpDir)), $output); } else { - $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), $url, $tmpDir), $output); + $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), $url, escapeshellarg($this->tmpDir)), $output); } $this->getTags(); From 23ef8a419a8f65fee30a1acc036929ef1ee52ed5 Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Wed, 25 Apr 2012 10:09:31 +0200 Subject: [PATCH 042/101] Fix var scope in HgDriver --- src/Composer/Repository/Vcs/HgDriver.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 08f34be9a..ddfdbe69b 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -31,13 +31,12 @@ class HgDriver extends VcsDriver */ public function initialize() { - $this->tmpDir = $this->config->get('home') . '/cache.hg/' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; + $this->tmpDir = $this->config->get('home') . '/cache.hg/' . preg_replace('{[^a-z0-9]}i', '-', $this->url) . '/'; - $url = escapeshellarg($this->url); if (is_dir($this->tmpDir)) { $this->process->execute(sprintf('cd %s && hg pull -u', escapeshellarg($this->tmpDir)), $output); } else { - $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), $url, escapeshellarg($this->tmpDir)), $output); + $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), escapeshellarg($this->url), escapeshellarg($this->tmpDir)), $output); } $this->getTags(); From 8b408449f6cd52c82354599b0ba4591f73c568de Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Wed, 25 Apr 2012 10:13:02 +0200 Subject: [PATCH 043/101] Create ~/.composer/cache.hg directory if not exists --- src/Composer/Repository/Vcs/HgDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index ddfdbe69b..a59407d48 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -36,7 +36,11 @@ class HgDriver extends VcsDriver if (is_dir($this->tmpDir)) { $this->process->execute(sprintf('cd %s && hg pull -u', escapeshellarg($this->tmpDir)), $output); } else { - $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(dirname($this->tmpDir)), escapeshellarg($this->url), escapeshellarg($this->tmpDir)), $output); + $dir = dirname($this->tmpDir); + if (!is_dir($dir)) { + mkdir($dir, 0777 & ~umask(), true); + } + $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg($dir), escapeshellarg($this->url), escapeshellarg($this->tmpDir)), $output); } $this->getTags(); From f0ea097134d9d20837d4203f50f952017b13716a Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Wed, 25 Apr 2012 10:13:02 +0200 Subject: [PATCH 044/101] Remove umask --- src/Composer/Repository/Vcs/HgDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index a59407d48..45186da7c 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -38,7 +38,7 @@ class HgDriver extends VcsDriver } else { $dir = dirname($this->tmpDir); if (!is_dir($dir)) { - mkdir($dir, 0777 & ~umask(), true); + mkdir($dir, 0777, true); } $this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg($dir), escapeshellarg($this->url), escapeshellarg($this->tmpDir)), $output); } From 1139b5c306ca7348c5aaac398d0bcb6f02da1a5d Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Wed, 25 Apr 2012 16:36:18 +0200 Subject: [PATCH 045/101] Protect composer home against web access --- src/Composer/Factory.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 2996c50a5..8929481c6 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -38,6 +38,11 @@ class Factory } } + // Protect directory against web access + if (!file_exists($home . '/.htaccess')) { + @mkdir($home, 0777, true) && @file_put_contents($home . '/.htaccess', 'deny from all'); + } + $config = new Config(); $file = new JsonFile($home.'/config.json'); From 610f15a768ec6fc4e896fff8ef913ce5d8a55475 Mon Sep 17 00:00:00 2001 From: Jerome Tamarelle Date: Wed, 25 Apr 2012 17:35:47 +0200 Subject: [PATCH 046/101] Fix unit tests for VcsRepository --- src/Composer/Repository/Vcs/GitHubDriver.php | 1 + src/Composer/Repository/Vcs/SvnDriver.php | 7 +++++-- .../Test/Repository/Vcs/GitHubDriverTest.php | 16 ++++++++++++---- .../Test/Repository/Vcs/SvnDriverTest.php | 3 ++- .../Test/Repository/VcsRepositoryTest.php | 3 ++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 61a0589ac..9091d0c6f 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -235,6 +235,7 @@ class GitHubDriver extends VcsDriver $this->gitDriver = new GitDriver( $this->generateSshUrl(), $this->io, + $this->config, $this->process, $this->remoteFilesystem ); diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 388b21f34..d431cb288 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -33,7 +33,7 @@ class SvnDriver extends VcsDriver /** * @var \Composer\Util\Svn */ - protected $util; + private $util; /** * Execute an SVN command and try to fix up the process with credentials @@ -46,6 +46,10 @@ class SvnDriver extends VcsDriver */ protected function execute($command, $url) { + if (null === $this->util) { + $this->util = new SvnUtil($this->baseUrl, $this->io, $this->process); + } + try { return $this->util->execute($command, $url); } catch (\RuntimeException $e) { @@ -65,7 +69,6 @@ class SvnDriver extends VcsDriver if (false !== ($pos = strrpos($this->url, '/trunk'))) { $this->baseUrl = substr($this->url, 0, $pos); } - $this->util = new SvnUtil($this->baseUrl, $this->io, $this->process); $this->getBranches(); $this->getTags(); diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index 845fb990f..88483bff8 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -15,6 +15,7 @@ namespace Composer\Test\Repository\Vcs; use Composer\Downloader\TransportException; use Composer\Repository\Vcs\GitHubDriver; use Composer\Util\Filesystem; +use Composer\Config; /** * @author Beau Simensen @@ -64,7 +65,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master"}')); - $gitHubDriver = new GitHubDriver($repoUrl, $io, null, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem); $gitHubDriver->initialize(); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); @@ -114,7 +115,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master"}')); - $gitHubDriver = new GitHubDriver($repoUrl, $io, null, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem); $gitHubDriver->initialize(); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); @@ -171,7 +172,14 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase // clean local clone if present $fs = new Filesystem(); - $fs->removeDirectory(sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9.]}i', '-', $repoSshUrl) . '/'); + $fs->removeDirectory(sys_get_temp_dir() . '/composer-test'); + + $config = new Config(); + $config->merge(array( + 'config' => array( + 'home' => sys_get_temp_dir() . '/composer-test', + ), + )); $process->expects($this->at(0)) ->method('execute') @@ -202,7 +210,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->method('splitLines') ->will($this->returnValue(array('* test_master'))); - $gitHubDriver = new GitHubDriver($repoUrl, $io, $process, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, $config, $process, $remoteFilesystem); $gitHubDriver->initialize(); $this->assertEquals('test_master', $gitHubDriver->getRootIdentifier()); diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 5399c394e..6f85faa0c 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\Repository\Vcs; use Composer\Repository\Vcs\SvnDriver; use Composer\IO\NullIO; +use Composer\Config; class SvnDriverTest extends \PHPUnit_Framework_TestCase { @@ -39,7 +40,7 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase ->method('getErrorOutput') ->will($this->returnValue($output)); - $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, $process); + $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, new Config(), $process); $svn->getTags(); } diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php index 9c09db0a2..c2060d5b7 100644 --- a/tests/Composer/Test/Repository/VcsRepositoryTest.php +++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php @@ -19,6 +19,7 @@ use Composer\Repository\Vcs\GitDriver; use Composer\Util\Filesystem; use Composer\Util\ProcessExecutor; use Composer\IO\NullIO; +use Composer\Config; class VcsRepositoryTest extends \PHPUnit_Framework_TestCase { @@ -123,7 +124,7 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase 'dev-master' => true, ); - $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO); + $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO, new Config()); $packages = $repo->getPackages(); $dumper = new ArrayDumper(); From 0e9f88a3da6374530a0c5b06a02729455690b7f5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 11:21:58 +0200 Subject: [PATCH 047/101] Fix DownloadManagerTest creating directories on disk --- src/Composer/Downloader/DownloadManager.php | 7 +++-- .../Test/Downloader/DownloadManagerTest.php | 30 ++++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index c2082350a..87d247568 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -24,6 +24,7 @@ use Composer\Util\Filesystem; class DownloadManager { private $preferSource = false; + private $filesystem; private $downloaders = array(); /** @@ -31,9 +32,10 @@ class DownloadManager * * @param Boolean $preferSource prefer downloading from source */ - public function __construct($preferSource = false) + public function __construct($preferSource = false, Filesystem $filesystem = null) { $this->preferSource = $preferSource; + $this->filesystem = $filesystem ?: new Filesystem(); } /** @@ -135,8 +137,7 @@ class DownloadManager throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } - $fs = new Filesystem(); - $fs->ensureDirectoryExists($targetDir); + $this->filesystem->ensureDirectoryExists($targetDir); $downloader = $this->getDownloaderForInstalledPackage($package); $downloader->download($package, $targetDir); diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index 9c1e2894c..29b2edf90 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -16,10 +16,17 @@ use Composer\Downloader\DownloadManager; class DownloadManagerTest extends \PHPUnit_Framework_TestCase { + protected $filesystem; + + public function setUp() + { + $this->filesystem = $this->getMock('Composer\Util\Filesystem'); + } + public function testSetGetDownloader() { $downloader = $this->createDownloaderMock(); - $manager = new DownloadManager(); + $manager = new DownloadManager(false, $this->filesystem); $manager->setDownloader('test', $downloader); $this->assertSame($downloader, $manager->getDownloader('test')); @@ -36,7 +43,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getInstallationSource') ->will($this->returnValue(null)); - $manager = new DownloadManager(); + $manager = new DownloadManager(false, $this->filesystem); $this->setExpectedException('InvalidArgumentException'); @@ -62,6 +69,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('dist')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -93,6 +101,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('source')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -126,6 +135,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('source')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -157,6 +167,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('dist')); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloader')) ->getMock(); @@ -195,6 +206,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -218,7 +230,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistType') ->will($this->returnValue(null)); - $manager = new DownloadManager(); + $manager = new DownloadManager(false, $this->filesystem); $this->setExpectedException('InvalidArgumentException'); $manager->download($package, 'target_dir'); @@ -248,6 +260,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -283,6 +296,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -318,6 +332,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -354,6 +369,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -390,6 +406,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'target_dir'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -414,7 +431,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistType') ->will($this->returnValue(null)); - $manager = new DownloadManager(); + $manager = new DownloadManager(false, $this->filesystem); $manager->setPreferSource(true); $this->setExpectedException('InvalidArgumentException'); @@ -450,6 +467,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)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager @@ -486,6 +504,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, 'vendor/bundles/FOS/UserBundle'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -526,6 +545,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, $target, 'vendor/pkg'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -562,6 +582,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($initial, 'vendor/pkg'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->getMock(); $manager @@ -588,6 +609,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->with($package, 'vendor/bundles/FOS/UserBundle'); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array(false, $this->filesystem)) ->setMethods(array('getDownloaderForInstalledPackage')) ->getMock(); $manager From ed8aa8322a20572f3d81187a72fd9e92b179736b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 11:23:34 +0200 Subject: [PATCH 048/101] Do not prevent updates if untracked files are present --- src/Composer/Downloader/GitDownloader.php | 2 +- tests/Composer/Test/Downloader/GitDownloaderTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 7f0e548c3..6af142809 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -61,7 +61,7 @@ class GitDownloader extends VcsDownloader */ protected function enforceCleanDirectory($path) { - $command = sprintf('cd %s && git status --porcelain', escapeshellarg($path)); + $command = sprintf('cd %s && git status --porcelain --untracked-files=no', escapeshellarg($path)); if (0 !== $this->process->execute($command, $output)) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index a64dddede..c23b95a7a 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -140,7 +140,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testUpdate() { $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'"); - $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) @@ -173,7 +173,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testUpdateThrowsRuntimeExceptionIfGitCommandFails() { $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'"); - $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) @@ -202,7 +202,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testRemove() { - $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); From 0995933183dba55fa98772179c20ce181ad76fbc Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Fri, 27 Apr 2012 11:42:58 +0200 Subject: [PATCH 049/101] Do not install root package as a vendor if some dependency requires it, closes #480 Also add some tests for the installer. --- src/Composer/Installer.php | 32 ++++- .../Installer/InstallationManager.php | 4 +- src/Composer/Repository/ArrayRepository.php | 7 ++ tests/Composer/Test/InstallerTest.php | 115 ++++++++++++++++++ .../Test/Mock/InstallationManagerMock.php | 65 ++++++++++ .../Test/Mock/WritableRepositoryMock.php | 26 ++++ tests/Composer/Test/TestCase.php | 15 +-- 7 files changed, 245 insertions(+), 19 deletions(-) create mode 100644 tests/Composer/Test/InstallerTest.php create mode 100644 tests/Composer/Test/Mock/InstallationManagerMock.php create mode 100644 tests/Composer/Test/Mock/WritableRepositoryMock.php diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 4ef8ea848..933d563e7 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -27,6 +27,7 @@ use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Locker; use Composer\Package\PackageInterface; +use Composer\Repository\ArrayRepository; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; @@ -76,6 +77,11 @@ class Installer */ protected $eventDispatcher; + /** + * @var AutoloadGenerator + */ + protected $autoloadGenerator; + protected $preferSource = false; protected $devMode = false; protected $dryRun = false; @@ -102,8 +108,9 @@ class Installer * @param Locker $locker * @param InstallationManager $installationManager * @param EventDispatcher $eventDispatcher + * @param AutoloadGenerator $autoloadGenerator */ - public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher) + public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, autoloadGenerator $autoloadGenerator) { $this->io = $io; $this->package = $package; @@ -112,6 +119,7 @@ class Installer $this->locker = $locker; $this->installationManager = $installationManager; $this->eventDispatcher = $eventDispatcher; + $this->autoloadGenerator = $autoloadGenerator; } /** @@ -128,7 +136,13 @@ class Installer } // create installed repo, this contains all local packages + platform packages (php & extensions) - $repos = array_merge($this->repositoryManager->getLocalRepositories(), array(new PlatformRepository())); + $repos = array_merge( + $this->repositoryManager->getLocalRepositories(), + array( + new ArrayRepository(array($this->package)), + new PlatformRepository(), + ) + ); $installedRepo = new CompositeRepository($repos); if ($this->additionalInstalledRepository) { $installedRepo->addRepository($this->additionalInstalledRepository); @@ -172,9 +186,8 @@ class Installer // write autoloader $this->io->write('Generating autoload files'); - $generator = new AutoloadGenerator; $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); - $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath() . '/composer', true); + $this->autoloadGenerator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath() . '/composer', true); // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; @@ -201,6 +214,10 @@ class Installer // creating requirements request $installFromLock = false; $request = new Request($pool); + + $constraint = new VersionConstraint('=', $this->package->getVersion()); + $request->install($this->package->getName(), $constraint); + if ($this->update) { $this->io->write('Updating '.($devMode ? 'dev ': '').'dependencies'); @@ -406,11 +423,13 @@ class Installer * @param IOInterface $io * @param Composer $composer * @param EventDispatcher $eventDispatcher + * @param AutoloadGenerator $autoloadGenerator * @return Installer */ - static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher = null) + static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher = null, AutoloadGenerator $autoloadGenerator = null) { $eventDispatcher = $eventDispatcher ?: new EventDispatcher($composer, $io); + $autoloadGenerator = $autoloadGenerator ?: new AutoloadGenerator; return new static( $io, @@ -419,7 +438,8 @@ class Installer $composer->getRepositoryManager(), $composer->getLocker(), $composer->getInstallationManager(), - $eventDispatcher + $eventDispatcher, + $autoloadGenerator ); } diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index e5080e0ef..3bf09de4f 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -33,7 +33,7 @@ class InstallationManager { private $installers = array(); private $cache = array(); - private $vendorPath; + protected $vendorPath; /** * Creates an instance of InstallationManager @@ -204,7 +204,7 @@ class InstallationManager } } - private function antiAlias(PackageInterface $package) + protected function antiAlias(PackageInterface $package) { if ($package instanceof AliasPackage) { $alias = $package; diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 1af73d311..7a6336393 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -25,6 +25,13 @@ class ArrayRepository implements RepositoryInterface { protected $packages; + public function __construct(array $packages = array()) + { + foreach ($packages as $package) { + $this->addPackage($package); + } + } + /** * {@inheritDoc} */ diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php new file mode 100644 index 000000000..179d0fe47 --- /dev/null +++ b/tests/Composer/Test/InstallerTest.php @@ -0,0 +1,115 @@ + + * 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\Installer; +use Composer\Repository\ArrayRepository; +use Composer\Repository\RepositoryManager; +use Composer\Repository\RepositoryInterface; +use Composer\Package\PackageInterface; +use Composer\Package\Link; +use Composer\Test\Mock\WritableRepositoryMock; +use Composer\Test\Mock\InstallationManagerMock; + +class InstallerTest extends TestCase +{ + /** + * @dataProvider provideInstaller + */ + public function testInstaller(array $expectedInstalled, array $expectedUpdated, array $expectedUninstalled, PackageInterface $package, RepositoryInterface $repository) + { + $io = $this->getMock('Composer\IO\IOInterface'); + + $package = $this->getPackage('A', '1.0.0'); + $package->setRequires(array( + new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), + )); + + $downloadManager = $this->getMock('Composer\Downloader\DownloadManager'); + $config = $this->getMock('Composer\Config'); + + $repositoryManager = new RepositoryManager($io, $config); + $repositoryManager->setLocalRepository(new WritableRepositoryMock()); + $repositoryManager->setLocalDevRepository(new WritableRepositoryMock()); + $repositoryManager->addRepository($repository); + + $locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock(); + $installationManager = new InstallationManagerMock(); + $eventDispatcher = $this->getMockBuilder('Composer\Script\EventDispatcher')->disableOriginalConstructor()->getMock(); + $autoloadGenerator = $this->getMock('Composer\Autoload\AutoloadGenerator'); + + $installer = new Installer($io, $package, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator); + $result = $installer->run(); + $this->assertTrue($result); + + $installed = $installationManager->getInstalledPackages(); + $this->assertSame($expectedInstalled, array_map(array($this, 'getPackageString'), $installed)); + + $updated = $installationManager->getUpdatedPackages(); + $this->assertSame($expectedUpdated, array_map(array($this, 'getPackageString'), $updated)); + + $uninstalled = $installationManager->getUninstalledPackages(); + $this->assertSame($expectedUninstalled, array_map(array($this, 'getPackageString'), $uninstalled)); + } + + public function provideInstaller() + { + $cases = array(); + + // when A requires B and B requires A, and A is a non-published root package + // the install of B should succeed + + $a = $this->getPackage('A', '1.0.0'); + $a->setRequires(array( + new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), + )); + $b = $this->getPackage('B', '1.0.0'); + $b->setRequires(array( + new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')), + )); + + $cases[] = array( + array('b-1.0.0.0'), + array(), + array(), + $a, + new ArrayRepository(array($b)), + ); + + // #480: when A requires B and B requires A, and A is a published root package + // only B should be installed, as A is the root + + $a = $this->getPackage('A', '1.0.0'); + $a->setRequires(array( + new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), + )); + $b = $this->getPackage('B', '1.0.0'); + $b->setRequires(array( + new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')), + )); + + $cases[] = array( + array('b-1.0.0.0'), + array(), + array(), + $a, + new ArrayRepository(array($a, $b)), + ); + + return $cases; + } + + public function getPackageString(PackageInterface $package) + { + return (string) $package; + } +} diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php new file mode 100644 index 000000000..8f0f83008 --- /dev/null +++ b/tests/Composer/Test/Mock/InstallationManagerMock.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\Test\Mock; + +use Composer\Installer\InstallationManager; +use Composer\Repository\RepositoryInterface; +use Composer\DependencyResolver\Operation\OperationInterface; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; + +class InstallationManagerMock extends InstallationManager +{ + private $installed = array(); + private $updated = array(); + private $uninstalled = array(); + + public function __construct($vendorDir = 'vendor') + { + $this->vendorPath = $vendorDir; + } + + public function install(RepositoryInterface $repo, InstallOperation $operation) + { + $package = $this->antiAlias($operation->getPackage()); + $this->installed[] = $package; + } + + public function update(RepositoryInterface $repo, UpdateOperation $operation) + { + $initial = $this->antiAlias($operation->getInitialPackage()); + $target = $this->antiAlias($operation->getTargetPackage()); + $this->updated[] = $target; + } + + public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) + { + $package = $this->antiAlias($operation->getPackage()); + $this->uninstalled[] = $package; + } + + public function getInstalledPackages() + { + return $this->installed; + } + + public function getUpdatedPackages() + { + return $this->updated; + } + + public function getUninstalledPackages() + { + return $this->uninstalled; + } +} diff --git a/tests/Composer/Test/Mock/WritableRepositoryMock.php b/tests/Composer/Test/Mock/WritableRepositoryMock.php new file mode 100644 index 000000000..59bb19f88 --- /dev/null +++ b/tests/Composer/Test/Mock/WritableRepositoryMock.php @@ -0,0 +1,26 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Mock; + +use Composer\Repository\ArrayRepository; +use Composer\Repository\WritableRepositoryInterface; + +class WritableRepositoryMock extends ArrayRepository implements WritableRepositoryInterface +{ + public function reload() + { + } + + public function write() + { + } +} diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index f0c8ab8fd..b2c2cc9cd 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -19,26 +19,19 @@ use Composer\Util\Filesystem; abstract class TestCase extends \PHPUnit_Framework_TestCase { - private static $versionParser; - - public static function setUpBeforeClass() - { - if (!self::$versionParser) { - self::$versionParser = new VersionParser(); - } - } - protected function getVersionConstraint($operator, $version) { + $versionParser = new VersionParser(); return new VersionConstraint( $operator, - self::$versionParser->normalize($version) + $versionParser->normalize($version) ); } protected function getPackage($name, $version) { - $normVersion = self::$versionParser->normalize($version); + $versionParser = new VersionParser(); + $normVersion = $versionParser->normalize($version); return new MemoryPackage($name, $normVersion, $version); } From 8ff497ac6fb56c41359581b596371aa1d23e2408 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 14:04:39 +0200 Subject: [PATCH 050/101] Skip filter_var on 5.3.2 since it is buggy --- src/Composer/Command/InitCommand.php | 2 +- src/Composer/Repository/ComposerRepository.php | 2 +- src/Composer/Repository/PearRepository.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index b1e778daa..02902adf7 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -35,7 +35,7 @@ class InitCommand extends Command public function parseAuthorString($author) { if (preg_match('/^(?P[- \.,\w\'’]+) <(?P.+?)>$/u', $author, $match)) { - if (!function_exists('filter_var') || $match['email'] === filter_var($match['email'], FILTER_VALIDATE_EMAIL)) { + if (!function_exists('filter_var') || version_compare(PHP_VERSION, '5.3.3', '<') || $match['email'] === filter_var($match['email'], FILTER_VALIDATE_EMAIL)) { return array( 'name' => trim($match['name']), 'email' => $match['email'] diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index f7e021e3e..938e7b566 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -40,7 +40,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $repoConfig['url'] = 'http://'.$repoConfig['url']; } $repoConfig['url'] = rtrim($repoConfig['url'], '/'); - if (function_exists('filter_var') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { + if (function_exists('filter_var') && version_compare(PHP_VERSION, '5.3.3', '>=') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']); } diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 14150d84d..dfd92e00d 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -39,7 +39,7 @@ class PearRepository extends ArrayRepository $repoConfig['url'] = 'http://'.$repoConfig['url']; } - if (function_exists('filter_var') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { + if (function_exists('filter_var') && version_compare(PHP_VERSION, '5.3.3', '>=') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']); } From 1e23354666045dc8fc0e4297a59e264bb5bdd312 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Fri, 27 Apr 2012 14:40:08 +0200 Subject: [PATCH 051/101] Do not suggest packages that were installed --- src/Composer/Installer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 4ef8ea848..5819e3996 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -154,7 +154,9 @@ class Installer // output suggestions foreach ($this->suggestedPackages as $suggestion) { - $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); + if (!$installedRepo->findPackages($suggestion['target'])) { + $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); + } } if (!$this->dryRun) { From b83422b05193f295823be220058d671f789d6a69 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Fri, 27 Apr 2012 16:34:09 +0200 Subject: [PATCH 052/101] [Installer] Fix removeBinaries, closes #567 --- 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 d690c357b..c75ebad95 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -186,7 +186,7 @@ class LibraryInstaller implements InstallerInterface if (!$package->getBinaries()) { return; } - foreach ($package->getBinaries() as $bin => $os) { + foreach ($package->getBinaries() as $bin) { $link = $this->binDir.'/'.basename($bin); if (!file_exists($link)) { continue; From aa4ececa2a10966ab6e63eafbc200218c2eb3f82 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Fri, 27 Apr 2012 16:55:43 +0200 Subject: [PATCH 053/101] [Installer] Make bin symlinks relative, closes #620 --- src/Composer/Installer/LibraryInstaller.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index c75ebad95..77c1c13fe 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -168,14 +168,18 @@ class LibraryInstaller implements InstallerInterface } file_put_contents($link, $this->generateWindowsProxyCode($bin, $link)); } else { + $cwd = getcwd(); try { // under linux symlinks are not always supported for example // when using it in smbfs mounted folder - symlink($bin, $link); + $relativeBin = $this->filesystem->findShortestPath($link, $bin); + chdir(dirname($link)); + symlink($relativeBin, $link); } catch (\ErrorException $e) { + var_dump($e->getMessage()); file_put_contents($link, $this->generateUnixyProxyCode($bin, $link)); } - + chdir($cwd); } chmod($link, 0777 & ~umask()); } From 8d71030cc137f63a20e4283736070b51c2b75c30 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Fri, 27 Apr 2012 17:07:41 +0200 Subject: [PATCH 054/101] [Installer] Classic var_dump must be removed --- src/Composer/Installer/LibraryInstaller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 77c1c13fe..0da923654 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -176,7 +176,6 @@ class LibraryInstaller implements InstallerInterface chdir(dirname($link)); symlink($relativeBin, $link); } catch (\ErrorException $e) { - var_dump($e->getMessage()); file_put_contents($link, $this->generateUnixyProxyCode($bin, $link)); } chdir($cwd); From 5ba9a887c2da12db82e1e8fa05f467fab6d36f5d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 17:40:59 +0200 Subject: [PATCH 055/101] We do not support any options to keep obsolete packages --- src/Composer/DependencyResolver/Solver.php | 63 +++++++++------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index aa014035c..7e8554a7d 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -30,7 +30,6 @@ class Solver protected $ruleToJob = array(); protected $addedMap = array(); protected $updateMap = array(); - protected $noObsoletes = array(); protected $watches = array(); protected $removeWatches = array(); protected $decisionMap; @@ -237,50 +236,38 @@ class Solver } // check obsoletes and implicit obsoletes of a package - // if ignoreinstalledsobsoletes is not set, we're also checking - // obsoletes of installed packages (like newer rpm versions) - // - /** TODO if ($this->noInstalledObsoletes) */ - if (true) { - $noObsoletes = isset($this->noObsoletes[$package->getId()]); - $isInstalled = (isset($this->installedMap[$package->getId()])); + $isInstalled = (isset($this->installedMap[$package->getId()])); - foreach ($package->getReplaces() as $link) { - $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); + foreach ($package->getReplaces() as $link) { + $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; - } - - $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; } + + $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); } + } - // check implicit obsoletes - // for installed packages we only need to check installed/installed problems, - // as the others are picked up when looking at the uninstalled package. - if (!$isInstalled) { - $obsoleteProviders = $this->pool->whatProvides($package->getName(), null); + // check implicit obsoletes + // for installed packages we only need to check installed/installed problems, + // as the others are picked up when looking at the uninstalled package. + if (!$isInstalled) { + $obsoleteProviders = $this->pool->whatProvides($package->getName(), null); - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; - } - - if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { - continue; - } - - // obsolete same packages even when noObsoletes - if ($noObsoletes && (!$package->equals($provider))) { - continue; - } - - $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, (string) $package)); + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; } + + if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { + continue; + } + + $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, (string) $package)); } } } From 0e537b9c93c105ad1f8ed21f2acd849357d4bbdf Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 17:44:42 +0200 Subject: [PATCH 056/101] We won't implement choice rules, you need to edit composer.json to choose --- src/Composer/DependencyResolver/RuleSet.php | 4 +- src/Composer/DependencyResolver/Solver.php | 174 ------------------ .../Test/DependencyResolver/RuleSetTest.php | 1 - 3 files changed, 1 insertion(+), 178 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 1f444c788..3278b1a98 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -21,15 +21,13 @@ class RuleSet implements \IteratorAggregate, \Countable const TYPE_PACKAGE = 0; const TYPE_JOB = 1; const TYPE_FEATURE = 3; - const TYPE_CHOICE = 4; - const TYPE_LEARNED = 5; + const TYPE_LEARNED = 4; protected static $types = array( -1 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', self::TYPE_FEATURE => 'FEATURE', self::TYPE_JOB => 'JOB', - self::TYPE_CHOICE => 'CHOICE', self::TYPE_LEARNED => 'LEARNED', ); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 7e8554a7d..f2ba5321b 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -483,174 +483,6 @@ class Solver } } - protected function addChoiceRules() - { - -// void -// solver_addchoicerules(Solver *solv) -// { -// Pool *pool = solv->pool; -// Map m, mneg; -// Rule *r; -// Queue q, qi; -// int i, j, rid, havechoice; -// Id p, d, *pp; -// Id p2, pp2; -// Solvable *s, *s2; -// -// solv->choicerules = solv->nrules; -// if (!pool->installed) -// { -// solv->choicerules_end = solv->nrules; -// return; -// } -// solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id)); -// queue_init(&q); -// queue_init(&qi); -// map_init(&m, pool->nsolvables); -// map_init(&mneg, pool->nsolvables); -// /* set up negative assertion map from infarch and dup rules */ -// for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++) -// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1)) -// MAPSET(&mneg, -r->p); -// for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++) -// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1)) -// MAPSET(&mneg, -r->p); -// for (rid = 1; rid < solv->rpmrules_end ; rid++) -// { -// r = solv->rules + rid; -// if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0)) -// continue; /* only look at requires rules */ -// // solver_printrule(solv, SAT_DEBUG_RESULT, r); -// queue_empty(&q); -// queue_empty(&qi); -// havechoice = 0; -// FOR_RULELITERALS(p, pp, r) -// { -// if (p < 0) -// continue; -// s = pool->solvables + p; -// if (!s->repo) -// continue; -// if (s->repo == pool->installed) -// { -// queue_push(&q, p); -// continue; -// } -// /* check if this package is "blocked" by a installed package */ -// s2 = 0; -// FOR_PROVIDES(p2, pp2, s->name) -// { -// s2 = pool->solvables + p2; -// if (s2->repo != pool->installed) -// continue; -// if (!pool->implicitobsoleteusesprovides && s->name != s2->name) -// continue; -// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) -// continue; -// break; -// } -// if (p2) -// { -// /* found installed package p2 that we can update to p */ -// if (MAPTST(&mneg, p)) -// continue; -// if (policy_is_illegal(solv, s2, s, 0)) -// continue; -// queue_push(&qi, p2); -// queue_push(&q, p); -// continue; -// } -// if (s->obsoletes) -// { -// Id obs, *obsp = s->repo->idarraydata + s->obsoletes; -// s2 = 0; -// while ((obs = *obsp++) != 0) -// { -// FOR_PROVIDES(p2, pp2, obs) -// { -// s2 = pool->solvables + p2; -// if (s2->repo != pool->installed) -// continue; -// if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs)) -// continue; -// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) -// continue; -// break; -// } -// if (p2) -// break; -// } -// if (obs) -// { -// /* found installed package p2 that we can update to p */ -// if (MAPTST(&mneg, p)) -// continue; -// if (policy_is_illegal(solv, s2, s, 0)) -// continue; -// queue_push(&qi, p2); -// queue_push(&q, p); -// continue; -// } -// } -// /* package p is independent of the installed ones */ -// havechoice = 1; -// } -// if (!havechoice || !q.count) -// continue; /* no choice */ -// -// /* now check the update rules of the installed package. -// * if all packages of the update rules are contained in -// * the dependency rules, there's no need to set up the choice rule */ -// map_empty(&m); -// FOR_RULELITERALS(p, pp, r) -// if (p > 0) -// MAPSET(&m, p); -// for (i = 0; i < qi.count; i++) -// { -// if (!qi.elements[i]) -// continue; -// Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start); -// if (!ur->p) -// ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start); -// if (!ur->p) -// continue; -// FOR_RULELITERALS(p, pp, ur) -// if (!MAPTST(&m, p)) -// break; -// if (p) -// break; -// for (j = i + 1; j < qi.count; j++) -// if (qi.elements[i] == qi.elements[j]) -// qi.elements[j] = 0; -// } -// if (i == qi.count) -// { -// #if 0 -// printf("skipping choice "); -// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid); -// #endif -// continue; -// } -// d = q.count ? pool_queuetowhatprovides(pool, &q) : 0; -// solver_addrule(solv, r->p, d); -// queue_push(&solv->weakruleq, solv->nrules - 1); -// solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid; -// #if 0 -// printf("OLD "); -// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid); -// printf("WEAK CHOICE "); -// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1); -// #endif -// } -// queue_free(&q); -// queue_free(&qi); -// map_free(&m); -// map_free(&mneg); -// solv->choicerules_end = solv->nrules; -// } - } - /*********************************************************************** *** *** Policy rule disabling/reenabling @@ -939,8 +771,6 @@ class Solver } } - $this->addChoiceRules(); - foreach ($this->rules as $rule) { $this->addWatchesToRule($rule); } @@ -1523,10 +1353,6 @@ class Solver $why = $lastWeakWhy; } - if ($lastWeakWhy->getType() == RuleSet::TYPE_CHOICE) { - $this->disableChoiceRules($lastWeakWhy); - } - $this->disableProblem($why); /** diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 54ad88a58..f974732dc 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -31,7 +31,6 @@ class RuleSetTest extends TestCase new Rule(array(), 'update1', null), ), RuleSet::TYPE_LEARNED => array(), - RuleSet::TYPE_CHOICE => array(), ); $ruleSet = new RuleSet; From 1a48ebaf577d832819b31ba316a82bd684309964 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 17:50:53 +0200 Subject: [PATCH 057/101] Create lookup table for installed packages in only one place --- src/Composer/DependencyResolver/Solver.php | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index f2ba5321b..15b1f46ea 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -35,6 +35,8 @@ class Solver protected $decisionMap; protected $installedMap; + protected $installedPackages; + protected $packageToFeatureRule = array(); public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) @@ -667,14 +669,20 @@ class Solver } } + protected function setupInstalledMap() + { + $this->installedPackages = $this->installed->getPackages(); + $this->installedMap = array(); + foreach ($this->installedPackages as $package) { + $this->installedMap[$package->getId()] = $package; + } + } + public function solve(Request $request) { $this->jobs = $request->getJobs(); - $installedPackages = $this->installed->getPackages(); - $this->installedMap = array(); - foreach ($installedPackages as $package) { - $this->installedMap[$package->getId()] = $package; - } + + $this->setupInstalledMap(); if (version_compare(PHP_VERSION, '5.3.4', '>=')) { $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1); @@ -695,18 +703,18 @@ class Solver switch ($job['cmd']) { case 'update-all': - foreach ($installedPackages as $package) { + foreach ($this->installedMap as $package) { $this->updateMap[$package->getId()] = true; } break; } } - foreach ($installedPackages as $package) { + foreach ($this->installedMap as $package) { $this->addRulesForPackage($package); } - foreach ($installedPackages as $package) { + foreach ($this->installedMap as $package) { $this->addRulesForUpdatePackages($package); } @@ -724,7 +732,7 @@ class Solver // solver_addrpmrulesforweak(solv, &addedmap); - foreach ($installedPackages as $package) { + foreach ($this->installedMap as $package) { $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); @@ -1467,8 +1475,6 @@ class Solver $minimizationSteps = 0; $installedPos = 0; - $this->installedPackages = $this->installed->getPackages(); - while (true) { if (1 === $level) { From 2cb2cde096ab2b2f4d3d0b5c36fb406ea6856abe Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:13:37 +0200 Subject: [PATCH 058/101] Installed packages are now always removed unless otherwise requested This means that an update request must always be accompanied by an install request, otherwise the package might be removed rather than updated. --- src/Composer/DependencyResolver/Solver.php | 135 ++---------------- .../Test/DependencyResolver/SolverTest.php | 31 +++- 2 files changed, 39 insertions(+), 127 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 15b1f46ea..0cb754284 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -730,14 +730,12 @@ class Solver } } - // solver_addrpmrulesforweak(solv, &addedmap); - foreach ($this->installedMap as $package) { $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); $rule->setWeak(true); - $this->addRule(RuleSet::TYPE_FEATURE, $rule); + //$this->addRule(RuleSet::TYPE_FEATURE, $rule); $this->packageToFeatureRule[$package->getId()] = $rule; } @@ -857,6 +855,18 @@ class Solver } } + foreach ($this->decisionMap as $packageId => $decision) { + if ($packageId === 0) { + continue; + } + + if (0 == $decision && isset($this->installedMap[$packageId])) { + $transaction[] = new Operation\UninstallOperation( + $this->pool->packageById($packageId), null + ); + } + } + return array_reverse($transaction); } @@ -1546,125 +1556,6 @@ class Solver } } - // handle installed packages - if ($level < $systemLevel) { - // use two passes if any packages are being updated - // -> better user experience - for ($pass = (count($this->updateMap)) ? 0 : 1; $pass < 2; $pass++) { - $passLevel = $level; - for ($i = $installedPos, $n = 0; $n < count($this->installedPackages); $i++, $n++) { - $repeat = false; - - if ($i == count($this->installedPackages)) { - $i = 0; - } - $literal = new Literal($this->installedPackages[$i], true); - - if ($this->decisionsContain($literal)) { - continue; - } - - // only process updates in first pass - /** TODO: && or || ? **/ - if (0 === $pass && !isset($this->updateMap[$literal->getPackageId()])) { - continue; - } - - $rule = null; - - if (isset($this->packageToFeatureRule[$literal->getPackageId()])) { - $rule = $this->packageToFeatureRule[$literal->getPackageId()]; - } - - if (!$rule || $rule->isDisabled()) { - continue; - } - - $updateRuleLiterals = $rule->getLiterals(); - - $decisionQueue = array(); - if (!isset($this->noUpdate[$literal->getPackageId()]) && ( - $this->decidedRemove($literal->getPackage()) || - isset($this->updateMap[$literal->getPackageId()]) || - !$literal->equals($updateRuleLiterals[0]) - )) { - foreach ($updateRuleLiterals as $ruleLiteral) { - if ($this->decidedInstall($ruleLiteral->getPackage())) { - // already fulfilled - $decisionQueue = array(); - break; - } - if ($this->undecided($ruleLiteral->getPackage())) { - $decisionQueue[] = $ruleLiteral; - } - } - } - - if (sizeof($decisionQueue)) { - $oLevel = $level; - $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule); - - if (0 === $level) { - return; - } - - if ($level <= $oLevel) { - $repeat = true; - } - } else if (!$repeat && $this->undecided($literal->getPackage())) { - // still undecided? keep package. - $oLevel = $level; - if (isset($this->cleanDepsMap[$literal->getPackageId()])) { - // clean deps removes package - $level = $this->setPropagateLearn($level, $literal->invert(), $disableRules, null); - } else { - // ckeeping package - $level = $this->setPropagateLearn($level, $literal, $disableRules, $rule); - } - - - if (0 === $level) { - return; - } - - if ($level <= $oLevel) { - $repeat = true; - } - } - - if ($repeat) { - if (1 === $level || $level < $passLevel) { - // trouble - break; - } - if ($level < $oLevel) { - // redo all - $n = 0; - } - - // repeat - $i--; - $n--; - continue; - } - } - - if ($n < count($this->installedPackages)) { - $installedPos = $i; // retry this problem next time - break; - } - - $installedPos = 0; - } - - $systemLevel = $level + 1; - - if ($pass < 2) { - // had trouble => retry - continue; - } - } - if ($level < $systemLevel) { $systemLevel = $level; } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 49355a5a4..2bc5a928a 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -54,6 +54,16 @@ class SolverTest extends TestCase )); } + public function testSolverRemoveIfNotInstalled() + { + $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->reposComplete(); + + $this->checkSolverResult(array( + array('job' => 'remove', 'package' => $packageA), + )); + } + public function testInstallNonExistingPackageFails() { $this->repo->addPackage($this->getPackage('A', '1.0')); @@ -176,6 +186,7 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1')); $this->reposComplete(); + $this->request->install('A'); $this->request->update('A'); $this->checkSolverResult(array( @@ -191,6 +202,7 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $packageA->setRequires(array(new Link('A', 'B', null, 'requires'))); + $newPackageA->setRequires(array(new Link('A', 'B', null, 'requires'))); $this->reposComplete(); @@ -209,6 +221,7 @@ class SolverTest extends TestCase $this->repo->addPackage($this->getPackage('A', '1.0')); $this->reposComplete(); + $this->request->install('A'); $this->request->update('A'); $this->checkSolverResult(array()); @@ -223,6 +236,8 @@ class SolverTest extends TestCase $this->reposComplete(); + $this->request->install('A'); + $this->request->install('B'); $this->request->update('A'); $this->checkSolverResult(array( @@ -267,7 +282,7 @@ class SolverTest extends TestCase public function testSolverUpdateFullyConstrainedPrunesInstalledPackages() { $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); - $this->repoInstalled->addPackage($this->getPackage('B', '1.0')); + $this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($newPackageA = $this->getPackage('A', '1.2')); $this->repo->addPackage($this->getPackage('A', '2.0')); $this->reposComplete(); @@ -275,10 +290,15 @@ class SolverTest extends TestCase $this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0')); $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0')); - $this->checkSolverResult(array(array( - 'job' => 'update', - 'from' => $packageA, - 'to' => $newPackageA, + $this->checkSolverResult(array( + array( + 'job' => 'remove', + 'package' => $packageB, + ), + array( + 'job' => 'update', + 'from' => $packageA, + 'to' => $newPackageA, ))); } @@ -297,6 +317,7 @@ class SolverTest extends TestCase $this->reposComplete(); $this->request->install('A'); + $this->request->install('C'); $this->request->update('C'); $this->request->remove('D'); From 3618ddacb06ebc3cb01a99634c83bb76c6e8df7a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:16:34 +0200 Subject: [PATCH 059/101] Remove commented out debug output --- src/Composer/DependencyResolver/Solver.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 0cb754284..6eb57b636 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -789,9 +789,6 @@ class Solver $installRecommended = 0; $this->runSat(true, $installRecommended); - //$this->printDecisionMap(); - //findrecommendedsuggested(solv); - //solver_prepare_solutions(solv); if ($this->problems) { throw new SolverProblemsException($this->problems); From 6410817c4e36ab5f2cc0bb2f7afdb7a1b8b64bd0 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:21:25 +0200 Subject: [PATCH 060/101] Policy rules cannot conflict with jobs anymore As we no longer need special feature rules for updating installed packages we don't have policy rules which might conflict with job rules anymore. Everything is driven by jobs now. --- src/Composer/DependencyResolver/Solver.php | 196 --------------------- 1 file changed, 196 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 6eb57b636..f692a2d22 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -481,191 +481,6 @@ class Solver } $this->disableProblem($why); - /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */ - } - } - -/*********************************************************************** - *** - *** Policy rule disabling/reenabling - *** - *** Disable all policy rules that conflict with our jobs. If a job - *** gets disabled later on, reenable the involved policy rules again. - *** - *** / - -#define DISABLE_UPDATE 1 -#define DISABLE_INFARCH 2 -#define DISABLE_DUP 3 -*/ - protected function jobToDisableQueue(array $job, array $disableQueue) - { - switch ($job['cmd']) { - case 'install': - foreach ($job['packages'] as $package) { - if (isset($this->installedMap[$package->getId()])) { - $disableQueue[] = array('type' => 'update', 'package' => $package); - } - - /* all job packages obsolete * / - qstart = q->count; - pass = 0; - memset(&omap, 0, sizeof(omap)); - FOR_JOB_SELECT(p, pp, select, what) - { - Id p2, pp2; - - if (pass == 1) - map_grow(&omap, installed->end - installed->start); - s = pool->solvables + p; - if (s->obsoletes) - { - Id obs, *obsp; - obsp = s->repo->idarraydata + s->obsoletes; - while ((obs = *obsp++) != 0) - FOR_PROVIDES(p2, pp2, obs) - { - Solvable *ps = pool->solvables + p2; - if (ps->repo != installed) - continue; - if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) - continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - if (pass) - MAPSET(&omap, p2 - installed->start); - else - queue_push2(q, DISABLE_UPDATE, p2); - } - } - FOR_PROVIDES(p2, pp2, s->name) - { - Solvable *ps = pool->solvables + p2; - if (ps->repo != installed) - continue; - if (!pool->implicitobsoleteusesprovides && ps->name != s->name) - continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - if (pass) - MAPSET(&omap, p2 - installed->start); - else - queue_push2(q, DISABLE_UPDATE, p2); - } - if (pass) - { - for (i = j = qstart; i < q->count; i += 2) - { - if (MAPTST(&omap, q->elements[i + 1] - installed->start)) - { - MAPCLR(&omap, q->elements[i + 1] - installed->start); - q->elements[j + 1] = q->elements[i + 1]; - j += 2; - } - } - queue_truncate(q, j); - } - if (q->count == qstart) - break; - pass++; - } - if (omap.size) - map_free(&omap); - - if (qstart == q->count) - return; /* nothing to prune * / - if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) - return; /* all is set */ - - /* now that we know which installed packages are obsoleted check each of them * / - for (i = j = qstart; i < q->count; i += 2) - { - Solvable *is = pool->solvables + q->elements[i + 1]; - FOR_JOB_SELECT(p, pp, select, what) - { - int illegal = 0; - s = pool->solvables + p; - if ((set & SOLVER_SETEVR) != 0) - illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * / - if ((set & SOLVER_SETARCH) != 0) - illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * / - if ((set & SOLVER_SETVENDOR) != 0) - illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * / - illegal = policy_is_illegal(solv, is, s, illegal); - if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0) - { - /* it's ok if the EV is different * / - if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0) - illegal = 0; - } - if (illegal) - break; - } - if (!p) - { - /* no package conflicts with the update rule * / - /* thus keep the DISABLE_UPDATE * / - q->elements[j + 1] = q->elements[i + 1]; - j += 2; - } - } - queue_truncate(q, j); - return;*/ - } - break; - - case 'remove': - foreach ($job['packages'] as $package) { - if (isset($this->installedMap[$package->getId()])) { - $disableQueue[] = array('type' => 'update', 'package' => $package); - } - } - break; - } - - return $disableQueue; - } - - protected function disableUpdateRule($package) - { - if (isset($this->packageToFeatureRule[$package->getId()])) { - $this->packageToFeatureRule[$package->getId()]->disable(); - } - } - - /** - * Disables all policy rules that conflict with jobs - */ - protected function disablePolicyRules() - { - $lastJob = null; - $allQueue = array(); - - $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB); - foreach ($iterator as $rule) { - if ($rule->isDisabled()) { - continue; - } - - $job = $this->ruleToJob[$rule->getId()]; - - if ($job === $lastJob) { - continue; - } - - $lastJob = $job; - - $allQueue = $this->jobToDisableQueue($job, $allQueue); - } - - foreach ($allQueue as $disable) { - switch ($disable['type']) { - case 'update': - $this->disableUpdateRule($disable['package']); - break; - default: - throw new \RuntimeException("Unsupported disable type: " . $disable['type']); - } } } @@ -734,8 +549,6 @@ class Solver $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); - $rule->setWeak(true); - //$this->addRule(RuleSet::TYPE_FEATURE, $rule); $this->packageToFeatureRule[$package->getId()] = $rule; } @@ -781,9 +594,6 @@ class Solver $this->addWatchesToRule($rule); } - /* disable update rules that conflict with our job */ - $this->disablePolicyRules(); - /* make decisions based on job/update assertions */ $this->makeAssertionRuleDecisions(); @@ -1369,12 +1179,6 @@ class Solver } $this->disableProblem($why); - - /** -@TODO what does v < 0 mean here? ($why == v) - if (v < 0) - solver_reenablepolicyrules(solv, -(v + 1)); -*/ $this->resetSolver(); return true; From 7be4b82ad28d82363ebe787309678ae335f75be1 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:22:55 +0200 Subject: [PATCH 061/101] Move all solver members to top of the file --- src/Composer/DependencyResolver/Solver.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index f692a2d22..b6cbdf4e0 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -39,6 +39,15 @@ class Solver protected $packageToFeatureRule = array(); + protected $decisionQueue = array(); + protected $decisionQueueWhy = array(); + protected $decisionQueueFree = array(); + protected $propagateIndex; + protected $branches = array(); + protected $problems = array(); + protected $learnedPool = array(); + protected $recommendsIndex; + public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) { $this->policy = $policy; @@ -677,15 +686,6 @@ class Solver return array_reverse($transaction); } - protected $decisionQueue = array(); - protected $decisionQueueWhy = array(); - protected $decisionQueueFree = array(); - protected $propagateIndex; - protected $branches = array(); - protected $problems = array(); - protected $learnedPool = array(); - protected $recommendsIndex; - protected function literalFromId($id) { $package = $this->pool->packageById(abs($id)); From f98ab2e491576212490ba8906f01f517491db09a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:24:17 +0200 Subject: [PATCH 062/101] An array of installed packages is no longer needed in the solver --- src/Composer/DependencyResolver/Solver.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index b6cbdf4e0..d51bbd55d 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -35,8 +35,6 @@ class Solver protected $decisionMap; protected $installedMap; - protected $installedPackages; - protected $packageToFeatureRule = array(); protected $decisionQueue = array(); @@ -495,9 +493,8 @@ class Solver protected function setupInstalledMap() { - $this->installedPackages = $this->installed->getPackages(); $this->installedMap = array(); - foreach ($this->installedPackages as $package) { + foreach ($this->installed->getPackages() as $package) { $this->installedMap[$package->getId()] = $package; } } @@ -1269,9 +1266,7 @@ class Solver // * here's the main loop: // * 1) propagate new decisions (only needed once) // * 2) fulfill jobs - // * 3) try to keep installed packages // * 4) fulfill all unresolved rules - // * 5) install recommended packages // * 6) minimalize solution if we had choices // * if we encounter a problem, we rewind to a safe level and restart // * with step 1 From d153ab3f8f8c79768b69f129b668abf542be359e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:25:38 +0200 Subject: [PATCH 063/101] Call feature rules update rules, as there is no difference in composer --- src/Composer/DependencyResolver/Solver.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index d51bbd55d..5990fcc59 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -35,7 +35,7 @@ class Solver protected $decisionMap; protected $installedMap; - protected $packageToFeatureRule = array(); + protected $packageToUpdateRule = array(); protected $decisionQueue = array(); protected $decisionQueueWhy = array(); @@ -555,7 +555,7 @@ class Solver $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); - $this->packageToFeatureRule[$package->getId()] = $rule; + $this->packageToUpdateRule[$package->getId()] = $rule; } foreach ($this->jobs as $job) { @@ -625,8 +625,8 @@ class Solver if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) { $literals = array(); - if (isset($this->packageToFeatureRule[$package->getId()])) { - $literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals()); + if (isset($this->packageToUpdateRule[$package->getId()])) { + $literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals()); } foreach ($literals as $updateLiteral) { From f4d5568937a699735a2cf05e94fc2e8ae8a4b9f4 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 27 Apr 2012 18:28:18 +0200 Subject: [PATCH 064/101] Rules of type feature are no longer needed --- src/Composer/DependencyResolver/RuleSet.php | 2 -- src/Composer/DependencyResolver/Solver.php | 2 +- .../RuleSetIteratorTest.php | 6 +++--- .../Test/DependencyResolver/RuleSetTest.php | 19 +++++++++---------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 3278b1a98..9e8ecf29b 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -20,13 +20,11 @@ class RuleSet implements \IteratorAggregate, \Countable // highest priority => lowest number const TYPE_PACKAGE = 0; const TYPE_JOB = 1; - const TYPE_FEATURE = 3; const TYPE_LEARNED = 4; protected static $types = array( -1 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', - self::TYPE_FEATURE => 'FEATURE', self::TYPE_JOB => 'JOB', self::TYPE_LEARNED => 'LEARNED', ); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 5990fcc59..afa8f6996 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -427,7 +427,7 @@ class Solver // push all of our rules (can only be feature or job rules) // asserting this literal on the problem stack - foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_FEATURE)) as $assertRule) { + foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) { if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) { continue; } diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 28db18131..56084f32a 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -27,7 +27,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_FEATURE => array( + RuleSet::TYPE_LEARNED => array( new Rule(array(), 'update1', null), ), RuleSet::TYPE_PACKAGE => array(), @@ -46,7 +46,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( $this->rules[RuleSet::TYPE_JOB][0], $this->rules[RuleSet::TYPE_JOB][1], - $this->rules[RuleSet::TYPE_FEATURE][0], + $this->rules[RuleSet::TYPE_LEARNED][0], ); $this->assertEquals($expected, $result); @@ -64,7 +64,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( RuleSet::TYPE_JOB, RuleSet::TYPE_JOB, - RuleSet::TYPE_FEATURE, + RuleSet::TYPE_LEARNED, ); $this->assertEquals($expected, $result); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index f974732dc..f319651ae 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -27,16 +27,15 @@ class RuleSetTest extends TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_FEATURE => array( + RuleSet::TYPE_LEARNED => array( new Rule(array(), 'update1', null), ), - RuleSet::TYPE_LEARNED => array(), ); $ruleSet = new RuleSet; $ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB); - $ruleSet->add($rules[RuleSet::TYPE_FEATURE][0], RuleSet::TYPE_FEATURE); + $ruleSet->add($rules[RuleSet::TYPE_LEARNED][0], RuleSet::TYPE_LEARNED); $ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB); $this->assertEquals($rules, $ruleSet->getRules()); @@ -79,7 +78,7 @@ class RuleSetTest extends TestCase $rule1 = new Rule(array(), 'job1', null); $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); + $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); $iterator = $ruleSet->getIterator(); @@ -95,9 +94,9 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); + $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); - $iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_FEATURE); + $iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_LEARNED); $this->assertSame($rule2, $iterator->current()); } @@ -109,7 +108,7 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); + $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); $iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB); @@ -141,7 +140,7 @@ class RuleSetTest extends TestCase ->method('equal') ->will($this->returnValue(false)); - $ruleSet->add($rule, RuleSet::TYPE_FEATURE); + $ruleSet->add($rule, RuleSet::TYPE_LEARNED); $this->assertTrue($ruleSet->containsEqual($rule)); $this->assertFalse($ruleSet->containsEqual($rule2)); @@ -154,9 +153,9 @@ class RuleSetTest extends TestCase $literal = new Literal($this->getPackage('foo', '2.1'), true); $rule = new Rule(array($literal), 'job1', null); - $ruleSet->add($rule, RuleSet::TYPE_FEATURE); + $ruleSet->add($rule, RuleSet::TYPE_JOB); - $this->assertContains('FEATURE : (+foo-2.1.0.0)', $ruleSet->__toString()); + $this->assertContains('JOB : (+foo-2.1.0.0)', $ruleSet->__toString()); } private function getRuleMock() From 66dc17bf39478dc685e0da3022316956470646f0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 18:42:34 +0200 Subject: [PATCH 065/101] Avoid adding duplicate aliases --- src/Composer/Package/Locker.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index e8037b464..a7b5bdcd1 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -90,11 +90,19 @@ class Locker foreach ($lockedPackages as $info) { $resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version']; + + // try to find the package in the local repo (best match) $package = $repo->findPackage($info['package'], $resolvedVersion); + // try to find the package in any repo if (!$package) { + $package = $this->repositoryManager->findPackage($info['package'], $resolvedVersion); + } + + // try to find the package in any repo (second pass without alias + rebuild alias since it disappeared) + if (!$package && !empty($info['alias'])) { $package = $this->repositoryManager->findPackage($info['package'], $info['version']); - if ($package && !empty($info['alias'])) { + if ($package) { $alias = new AliasPackage($package, $info['alias'], $info['alias']); $package->getRepository()->addPackage($alias); $package = $alias; From b356c30c5c5fb199b96199bfa0dd43cafd862a21 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 20:53:32 +0300 Subject: [PATCH 066/101] Remove rogue. dot --- 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 b920eb455..32ce307ec 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -108,7 +108,7 @@ same version of the dependencies. If no `composer.json` lock file exists, it will read the dependencies and versions from `composer.json` and create the lock file. -This means that if any of the dependencies get a new version, you won't get the updates. +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 the latest matching versions (according to your `composer.json` file) and also update the lock file with the new version. From b99f9bae6045d87621df4474b4d8b3da2087bbdd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 19:23:35 +0200 Subject: [PATCH 067/101] Let the user know a package is being removed --- src/Composer/Downloader/FileDownloader.php | 1 + src/Composer/Downloader/VcsDownloader.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 356934514..b11bf7ba6 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -101,6 +101,7 @@ class FileDownloader implements DownloaderInterface */ public function remove(PackageInterface $package, $path) { + $this->io->write(" - Removing package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 2d19bba40..7d3c90c7f 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -77,6 +77,7 @@ abstract class VcsDownloader implements DownloaderInterface public function remove(PackageInterface $package, $path) { $this->enforceCleanDirectory($path); + $this->io->write(" - Removing package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } From 22a825fc07c6e011f0ee1fd90f1f930edd160c93 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 21:23:07 +0200 Subject: [PATCH 068/101] Clean up vendor dir after a package was removed --- src/Composer/Installer/LibraryInstaller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 0da923654..9be2e1d1d 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -127,6 +127,13 @@ class LibraryInstaller implements InstallerInterface $this->downloadManager->remove($package, $downloadPath); $this->removeBinaries($package); $repo->removePackage($package); + + if (strpos($package->getName(), '/')) { + $packageVendorDir = dirname($downloadPath); + if (is_dir($packageVendorDir) && !glob($packageVendorDir.'/*')) { + @rmdir($packageVendorDir); + } + } } /** From b3659d85f3d51c47bb342c30ba4cc4809197e018 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 21:24:07 +0200 Subject: [PATCH 069/101] Force platform packages to remain installed --- src/Composer/Installer.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 5819e3996..a6094d6e9 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -242,9 +242,11 @@ class Installer } } - // fix the version all installed packages that are not in the current local repo to prevent rogue updates + // fix the version of all installed packages (+ platform) that are not + // in the current local repo to prevent rogue updates (e.g. non-dev + // updating when in dev) foreach ($installedRepo->getPackages() as $package) { - if ($package->getRepository() === $localRepo || $package->getRepository() instanceof PlatformRepository) { + if ($package->getRepository() === $localRepo) { continue; } From d5e35fbe19be36cfdd0121398b5d9b85faf2854b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 21:40:46 +0200 Subject: [PATCH 070/101] Clarify output --- src/Composer/Downloader/FileDownloader.php | 4 ++-- src/Composer/Downloader/VcsDownloader.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index b11bf7ba6..3e15acf80 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -64,7 +64,7 @@ class FileDownloader implements DownloaderInterface $fileName = $this->getFileName($package, $path); - $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $processUrl = $this->processUrl($url); @@ -101,7 +101,7 @@ class FileDownloader implements DownloaderInterface */ public function remove(PackageInterface $package, $path) { - $this->io->write(" - Removing package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 7d3c90c7f..cacc74fc4 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -50,7 +50,7 @@ abstract class VcsDownloader implements DownloaderInterface throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information'); } - $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $this->filesystem->removeDirectory($path); $this->doDownload($package, $path); $this->io->write(''); @@ -65,7 +65,7 @@ abstract class VcsDownloader implements DownloaderInterface throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information'); } - $this->io->write(" - Package " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); $this->enforceCleanDirectory($path); $this->doUpdate($initial, $target, $path); $this->io->write(''); @@ -77,7 +77,7 @@ abstract class VcsDownloader implements DownloaderInterface public function remove(PackageInterface $package, $path) { $this->enforceCleanDirectory($path); - $this->io->write(" - Removing package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } From e982c19bdb38852ca15af7d123de1520c13b54b4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 27 Apr 2012 22:40:18 +0200 Subject: [PATCH 071/101] Add fields to sample satis config --- doc/articles/handling-private-packages-with-satis.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/articles/handling-private-packages-with-satis.md b/doc/articles/handling-private-packages-with-satis.md index c2e96ebc9..d615e0178 100644 --- a/doc/articles/handling-private-packages-with-satis.md +++ b/doc/articles/handling-private-packages-with-satis.md @@ -23,6 +23,8 @@ uses `"require-all": true` which selects all versions of all packages in the repositories you defined. { + "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" }, From d44b81527bc8e3ba2541118263364ca10e63744c Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sun, 29 Apr 2012 18:07:16 +1000 Subject: [PATCH 072/101] Fixed variable reference in verbose pear repository message --- src/Composer/Repository/PearRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index dfd92e00d..59a0707ce 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -74,7 +74,7 @@ class PearRepository extends ArrayRepository } $this->addPackage($loader->load($rev)); if ($this->io->isVerbose()) { - $this->io->write('Loaded '.$packageData['name'].' '.$packageData['version']); + $this->io->write('Loaded '.$rev['name'].' '.$rev['version']); } } } From d60188903dd052a668cb8d02e42319e7e89ea29c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 16:07:47 +0200 Subject: [PATCH 073/101] Add warning for PHP <5.3.2 users that didn't use the installer --- src/Composer/Console/Application.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index bcdd1817d..ae7518618 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -67,6 +67,10 @@ class Application extends BaseApplication $this->registerCommands(); $this->io = new ConsoleIO($input, $output, $this->getHelperSet()); + if (version_compare(PHP_VERSION, '5.3.2', '<')) { + $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.'); + } + return parent::doRun($input, $output); } From bd50ad32f0484127dd8d2206ff964c58c698b811 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 17:27:37 +0200 Subject: [PATCH 074/101] Refactor InstallationManagerMock --- src/Composer/Installer/InstallationManager.php | 6 +++--- .../Test/Mock/InstallationManagerMock.php | 15 +++------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 3bf09de4f..32587a8db 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -33,7 +33,7 @@ class InstallationManager { private $installers = array(); private $cache = array(); - protected $vendorPath; + private $vendorPath; /** * Creates an instance of InstallationManager @@ -197,14 +197,14 @@ class InstallationManager return getcwd().DIRECTORY_SEPARATOR.$this->vendorPath; } - protected function notifyInstall(PackageInterface $package) + private function notifyInstall(PackageInterface $package) { if ($package->getRepository() instanceof NotifiableRepositoryInterface) { $package->getRepository()->notifyInstall($package); } } - protected function antiAlias(PackageInterface $package) + private function antiAlias(PackageInterface $package) { if ($package instanceof AliasPackage) { $alias = $package; diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php index 8f0f83008..9a7af786b 100644 --- a/tests/Composer/Test/Mock/InstallationManagerMock.php +++ b/tests/Composer/Test/Mock/InstallationManagerMock.php @@ -24,28 +24,19 @@ class InstallationManagerMock extends InstallationManager private $updated = array(); private $uninstalled = array(); - public function __construct($vendorDir = 'vendor') - { - $this->vendorPath = $vendorDir; - } - public function install(RepositoryInterface $repo, InstallOperation $operation) { - $package = $this->antiAlias($operation->getPackage()); - $this->installed[] = $package; + $this->installed[] = $operation->getPackage(); } public function update(RepositoryInterface $repo, UpdateOperation $operation) { - $initial = $this->antiAlias($operation->getInitialPackage()); - $target = $this->antiAlias($operation->getTargetPackage()); - $this->updated[] = $target; + $this->updated[] = array($operation->getInitialPackage(), $operation->getTargetPackage()); } public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) { - $package = $this->antiAlias($operation->getPackage()); - $this->uninstalled[] = $package; + $this->uninstalled[] = $operation->getPackage(); } public function getInstalledPackages() From 63c6c6d8759dc570d4639a7ae7f098dac909a0e8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 17:28:35 +0200 Subject: [PATCH 075/101] Cache the version parser used in TestCase --- tests/Composer/Test/TestCase.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index b2c2cc9cd..3756e9fa3 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -19,19 +19,29 @@ use Composer\Util\Filesystem; abstract class TestCase extends \PHPUnit_Framework_TestCase { + private static $parser; + + protected static function getVersionParser() + { + if (!self::$parser) { + self::$parser = new VersionParser(); + } + + return self::$parser; + } + protected function getVersionConstraint($operator, $version) { - $versionParser = new VersionParser(); return new VersionConstraint( $operator, - $versionParser->normalize($version) + self::getVersionParser()->normalize($version) ); } protected function getPackage($name, $version) { - $versionParser = new VersionParser(); - $normVersion = $versionParser->normalize($version); + $normVersion = self::getVersionParser()->normalize($version); + return new MemoryPackage($name, $normVersion, $version); } From 7b2d3ff6a2ce8345f0766a013466dff55c792a91 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 17:29:06 +0200 Subject: [PATCH 076/101] Refactor InstallerTest to be a bit more future proof --- tests/Composer/Test/InstallerTest.php | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 179d0fe47..478815851 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -25,40 +25,45 @@ class InstallerTest extends TestCase /** * @dataProvider provideInstaller */ - public function testInstaller(array $expectedInstalled, array $expectedUpdated, array $expectedUninstalled, PackageInterface $package, RepositoryInterface $repository) + public function testInstaller(PackageInterface $rootPackage, $repositories, array $options) { $io = $this->getMock('Composer\IO\IOInterface'); - $package = $this->getPackage('A', '1.0.0'); - $package->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), - )); - $downloadManager = $this->getMock('Composer\Downloader\DownloadManager'); $config = $this->getMock('Composer\Config'); $repositoryManager = new RepositoryManager($io, $config); $repositoryManager->setLocalRepository(new WritableRepositoryMock()); $repositoryManager->setLocalDevRepository(new WritableRepositoryMock()); - $repositoryManager->addRepository($repository); + + if (!is_array($repositories)) { + $repositories = array($repositories); + } + foreach ($repositories as $repository) { + $repositoryManager->addRepository($repository); + } $locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock(); $installationManager = new InstallationManagerMock(); $eventDispatcher = $this->getMockBuilder('Composer\Script\EventDispatcher')->disableOriginalConstructor()->getMock(); $autoloadGenerator = $this->getMock('Composer\Autoload\AutoloadGenerator'); - $installer = new Installer($io, $package, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator); + $installer = new Installer($io, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator); $result = $installer->run(); $this->assertTrue($result); + $expectedInstalled = isset($options['install']) ? $options['install'] : array(); + $expectedUpdated = isset($options['update']) ? $options['update'] : array(); + $expectedUninstalled = isset($options['uninstall']) ? $options['uninstall'] : array(); + $installed = $installationManager->getInstalledPackages(); - $this->assertSame($expectedInstalled, array_map(array($this, 'getPackageString'), $installed)); + $this->assertSame($expectedInstalled, $installed); $updated = $installationManager->getUpdatedPackages(); - $this->assertSame($expectedUpdated, array_map(array($this, 'getPackageString'), $updated)); + $this->assertSame($expectedUpdated, $updated); $uninstalled = $installationManager->getUninstalledPackages(); - $this->assertSame($expectedUninstalled, array_map(array($this, 'getPackageString'), $uninstalled)); + $this->assertSame($expectedUninstalled, $uninstalled); } public function provideInstaller() @@ -78,11 +83,11 @@ class InstallerTest extends TestCase )); $cases[] = array( - array('b-1.0.0.0'), - array(), - array(), $a, new ArrayRepository(array($b)), + array( + 'install' => array($b) + ), ); // #480: when A requires B and B requires A, and A is a published root package @@ -98,18 +103,13 @@ class InstallerTest extends TestCase )); $cases[] = array( - array('b-1.0.0.0'), - array(), - array(), $a, new ArrayRepository(array($a, $b)), + array( + 'install' => array($b) + ), ); return $cases; } - - public function getPackageString(PackageInterface $package) - { - return (string) $package; - } } From adb4188e122a0318e6d6bfe8fdfd9501d56d8247 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 18:03:54 +0200 Subject: [PATCH 077/101] Add detection of the current version from git if possible --- .../Package/Loader/RootPackageLoader.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 734e28c1a..fcd35b16a 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -14,6 +14,7 @@ namespace Composer\Package\Loader; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryManager; +use Composer\Util\ProcessExecutor; /** * ArrayLoader built for the sole purpose of loading the root package @@ -25,10 +26,12 @@ use Composer\Repository\RepositoryManager; class RootPackageLoader extends ArrayLoader { private $manager; + private $process; - public function __construct(RepositoryManager $manager, VersionParser $parser = null) + public function __construct(RepositoryManager $manager, VersionParser $parser = null, ProcessExecutor $process = null) { $this->manager = $manager; + $this->process = $process ?: new ProcessExecutor(); parent::__construct($parser); } @@ -38,7 +41,25 @@ class RootPackageLoader extends ArrayLoader $config['name'] = '__root__'; } if (!isset($config['version'])) { - $config['version'] = '1.0.0'; + $version = '1.0.0'; + + // try to fetch current version from git branch + if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) { + foreach ($this->process->splitLines($output) as $branch) { + if ($branch && preg_match('{^(?:\* ) *(?:[^/ ]+?/)?(\S+) *[a-f0-9]+ .*$}', $branch, $match)) { + $version = 'dev-'.$match[1]; + if (isset($config['extra']['branch-alias'][$version]) + && substr($config['extra']['branch-alias'][$version], -4) === '-dev' + ) { + $targetBranch = $config['extra']['branch-alias'][$version]; + $normalized = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); + $version = preg_replace('{(\.9{7})+}', '.x', $normalized); + } + } + } + } + + $config['version'] = $version; } $package = parent::load($config); From 43968c23f9366c4b6bce4cafa3beb4a950c35df1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 18:28:48 +0200 Subject: [PATCH 078/101] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe7e78c13..d56b1f2ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,15 @@ * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint * Break: vendor/.composer/autoload.php has been moved to vendor/autoload.php, other files are now in vendor/composer/ * Added caching of repository metadata (faster startup times & failover if packagist is down) + * Added removal of packages that are not needed anymore * Added include_path support for legacy projects that are full of require_once statements * Added installation notifications API to allow better statistics on Composer repositories * Added autoloading support for root packages that use target-dir + * Added awareness of the root package presence and support for it's provide/replace/conflict keys + * Added IOInterface::isDecorated to test for colored output support * Improved repository protocol to have large cacheable parts + * Fixed various bugs relating to package aliasing, proxy configuration, binaries + * Various bug fixes and docs improvements * 1.0.0-alpha2 (2012-04-03) From b3164de813204dc66c4750fba017e600a6786330 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 20:43:56 +0200 Subject: [PATCH 079/101] Fix tests --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index d431cb288..7b2b5cea7 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -64,7 +64,7 @@ class SvnDriver extends VcsDriver */ public function initialize() { - $this->url = rtrim(self::normalizeUrl($this->url), '/'); + $this->url = $this->baseUrl = rtrim(self::normalizeUrl($this->url), '/'); if (false !== ($pos = strrpos($this->url, '/trunk'))) { $this->baseUrl = substr($this->url, 0, $pos); diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 6f85faa0c..569fab69f 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -41,7 +41,7 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($output)); $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, new Config(), $process); - $svn->getTags(); + $svn->initialize(); } private function getCmd($cmd) From fded3d8bdd275900667cd66ef2958c8d23fc38b7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 20:44:05 +0200 Subject: [PATCH 080/101] Update vendors --- composer.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.lock b/composer.lock index 2a5f4ee4f..bb9093141 100644 --- a/composer.lock +++ b/composer.lock @@ -12,19 +12,19 @@ { "package": "symfony/console", "version": "dev-master", - "source-reference": "8c69d3a3e26885cad132a38ab0d7f287fb975296", + "source-reference": "eaad4427b10ff39402bce0ae4f8cd1faf2b6532a", "alias": "2.1.9999999.9999999-dev" }, { "package": "symfony/finder", "version": "dev-master", - "source-reference": "5b8658e52eaf424fe943a5857e2404ce95c3b90e", + "source-reference": "78b2e33951821b6d423718f57788f1894dcb935a", "alias": "2.1.9999999.9999999-dev" }, { "package": "symfony/process", "version": "dev-master", - "source-reference": "3f95a0126588f00d70e745c1ef96631fb10c3a8e", + "source-reference": "718655f4bc664d693b33f3e6e8a895e454208021", "alias": "2.1.9999999.9999999-dev" } ], From b0b3e17a374d15bfedb0d6a404aba2a20863b798 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 20:46:15 +0200 Subject: [PATCH 081/101] Report errors when a git repo can not be synced up --- 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 94ccb61ce..c87654fd2 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -40,7 +40,9 @@ 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)) { - $this->process->execute('git remote update --prune origin', $output, $this->repoDir); + if (0 !== $this->process->execute('git remote update --prune origin', $output, $this->repoDir)) { + throw new \RuntimeException('Failed to update '.$this->url.', could not read packages from it' . "\n\n" .$this->process->getErrorOutput()); + } } else { // clean up directory and do a fresh clone into it $fs = new Filesystem(); From 58276d0a52303e8fe9feed785ef999028eb56914 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 20:55:05 +0200 Subject: [PATCH 082/101] Warn the user but do not fail hard when a repo can not be synced --- 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 c87654fd2..b33d75d85 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -41,7 +41,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 (0 !== $this->process->execute('git remote update --prune origin', $output, $this->repoDir)) { - throw new \RuntimeException('Failed to update '.$this->url.', could not read packages from it' . "\n\n" .$this->process->getErrorOutput()); + $this->io->write('Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')'); } } else { // clean up directory and do a fresh clone into it From a30e6109bf24ca5b9ad93c399cd70f2065115d0d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 21:04:18 +0200 Subject: [PATCH 083/101] Fix 5.4.0 interop in formatting of empty array/objects --- src/Composer/Json/JsonFile.php | 2 +- tests/Composer/Test/Json/JsonFileTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 695679d1d..16d3d3394 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -237,7 +237,7 @@ class JsonFile } } else { // Collapse empty {} and [] - $result = rtrim($result); + $result = rtrim($result)."\n\n".$indentStr; } } diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 4e30928cb..159017aa6 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -128,6 +128,20 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase $this->assertJsonFormat($json, $data); } + public function testFormatEmptyArray() + { + $data = array('test' => array(), 'test2' => new \stdClass); + $json = '{ + "test": [ + + ], + "test2": { + + } +}'; + $this->assertJsonFormat($json, $data); + } + public function testEscape() { $data = array("Metadata\\\"" => 'src/'); From f3df4772f9711babb4240c74fdb7fc740c1b29b4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 21:06:15 +0200 Subject: [PATCH 084/101] Make sure htaccess is created --- src/Composer/Factory.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 8929481c6..4cad60d97 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -40,7 +40,10 @@ class Factory // Protect directory against web access if (!file_exists($home . '/.htaccess')) { - @mkdir($home, 0777, true) && @file_put_contents($home . '/.htaccess', 'deny from all'); + if (!is_dir($home)) { + @mkdir($home, 0777, true); + } + @file_put_contents($home . '/.htaccess', 'Deny from all'); } $config = new Config(); From a4c23374331230fe79bf8c4659b4a02002705ed5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 21:16:24 +0200 Subject: [PATCH 085/101] Load root packages as aliases if they are aliased so that they replace everything --- .../Package/Loader/RootPackageLoader.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index fcd35b16a..517e44e51 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -15,6 +15,7 @@ namespace Composer\Package\Loader; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; +use Composer\Package\AliasPackage; /** * ArrayLoader built for the sole purpose of loading the root package @@ -48,13 +49,6 @@ class RootPackageLoader extends ArrayLoader foreach ($this->process->splitLines($output) as $branch) { if ($branch && preg_match('{^(?:\* ) *(?:[^/ ]+?/)?(\S+) *[a-f0-9]+ .*$}', $branch, $match)) { $version = 'dev-'.$match[1]; - if (isset($config['extra']['branch-alias'][$version]) - && substr($config['extra']['branch-alias'][$version], -4) === '-dev' - ) { - $targetBranch = $config['extra']['branch-alias'][$version]; - $normalized = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); - $version = preg_replace('{(\.9{7})+}', '.x', $normalized); - } } } } @@ -97,6 +91,16 @@ class RootPackageLoader extends ArrayLoader $package->setRepositories($config['repositories']); } + if (isset($config['extra']['branch-alias'][$version]) + && substr($config['extra']['branch-alias'][$version], -4) === '-dev' + ) { + $targetBranch = $config['extra']['branch-alias'][$version]; + $normalized = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); + $version = preg_replace('{(\.9{7})+}', '.x', $normalized); + + return new AliasPackage($package, $normalized, $version); + } + return $package; } } From f7440076fa4c23b6be77c8c89979e26b14a69d45 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 29 Apr 2012 22:21:58 +0200 Subject: [PATCH 086/101] Revert "Merge remote-tracking branch 'naderman/remove-packages-by-default'" This reverts commit 298815260246408dc93de66af83d0d9c6a3b24b7, reversing changes made to adb4188e122a0318e6d6bfe8fdfd9501d56d8247. --- src/Composer/DependencyResolver/RuleSet.php | 6 +- src/Composer/DependencyResolver/Solver.php | 616 ++++++++++++++++-- src/Composer/Downloader/FileDownloader.php | 3 +- src/Composer/Downloader/VcsDownloader.php | 5 +- src/Composer/Installer.php | 6 +- src/Composer/Installer/LibraryInstaller.php | 7 - .../RuleSetIteratorTest.php | 6 +- .../Test/DependencyResolver/RuleSetTest.php | 20 +- .../Test/DependencyResolver/SolverTest.php | 31 +- 9 files changed, 584 insertions(+), 116 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 9e8ecf29b..1f444c788 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -20,12 +20,16 @@ class RuleSet implements \IteratorAggregate, \Countable // highest priority => lowest number const TYPE_PACKAGE = 0; const TYPE_JOB = 1; - const TYPE_LEARNED = 4; + const TYPE_FEATURE = 3; + const TYPE_CHOICE = 4; + const TYPE_LEARNED = 5; protected static $types = array( -1 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', + self::TYPE_FEATURE => 'FEATURE', self::TYPE_JOB => 'JOB', + self::TYPE_CHOICE => 'CHOICE', self::TYPE_LEARNED => 'LEARNED', ); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index afa8f6996..aa014035c 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -30,21 +30,13 @@ class Solver protected $ruleToJob = array(); protected $addedMap = array(); protected $updateMap = array(); + protected $noObsoletes = array(); protected $watches = array(); protected $removeWatches = array(); protected $decisionMap; protected $installedMap; - protected $packageToUpdateRule = array(); - - protected $decisionQueue = array(); - protected $decisionQueueWhy = array(); - protected $decisionQueueFree = array(); - protected $propagateIndex; - protected $branches = array(); - protected $problems = array(); - protected $learnedPool = array(); - protected $recommendsIndex; + protected $packageToFeatureRule = array(); public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) { @@ -245,38 +237,50 @@ class Solver } // check obsoletes and implicit obsoletes of a package - $isInstalled = (isset($this->installedMap[$package->getId()])); + // if ignoreinstalledsobsoletes is not set, we're also checking + // obsoletes of installed packages (like newer rpm versions) + // + /** TODO if ($this->noInstalledObsoletes) */ + if (true) { + $noObsoletes = isset($this->noObsoletes[$package->getId()]); + $isInstalled = (isset($this->installedMap[$package->getId()])); - foreach ($package->getReplaces() as $link) { - $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); + foreach ($package->getReplaces() as $link) { + $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; + } + + $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); } - - $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); } - } - // check implicit obsoletes - // for installed packages we only need to check installed/installed problems, - // as the others are picked up when looking at the uninstalled package. - if (!$isInstalled) { - $obsoleteProviders = $this->pool->whatProvides($package->getName(), null); + // check implicit obsoletes + // for installed packages we only need to check installed/installed problems, + // as the others are picked up when looking at the uninstalled package. + if (!$isInstalled) { + $obsoleteProviders = $this->pool->whatProvides($package->getName(), null); - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; + } + + if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { + continue; + } + + // obsolete same packages even when noObsoletes + if ($noObsoletes && (!$package->equals($provider))) { + continue; + } + + $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, (string) $package)); } - - if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { - continue; - } - - $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, (string) $package)); } } } @@ -427,7 +431,7 @@ class Solver // push all of our rules (can only be feature or job rules) // asserting this literal on the problem stack - foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) { + foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_FEATURE)) as $assertRule) { if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) { continue; } @@ -488,22 +492,370 @@ class Solver } $this->disableProblem($why); + /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */ } } - protected function setupInstalledMap() + protected function addChoiceRules() { - $this->installedMap = array(); - foreach ($this->installed->getPackages() as $package) { - $this->installedMap[$package->getId()] = $package; + +// void +// solver_addchoicerules(Solver *solv) +// { +// Pool *pool = solv->pool; +// Map m, mneg; +// Rule *r; +// Queue q, qi; +// int i, j, rid, havechoice; +// Id p, d, *pp; +// Id p2, pp2; +// Solvable *s, *s2; +// +// solv->choicerules = solv->nrules; +// if (!pool->installed) +// { +// solv->choicerules_end = solv->nrules; +// return; +// } +// solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id)); +// queue_init(&q); +// queue_init(&qi); +// map_init(&m, pool->nsolvables); +// map_init(&mneg, pool->nsolvables); +// /* set up negative assertion map from infarch and dup rules */ +// for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++) +// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1)) +// MAPSET(&mneg, -r->p); +// for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++) +// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1)) +// MAPSET(&mneg, -r->p); +// for (rid = 1; rid < solv->rpmrules_end ; rid++) +// { +// r = solv->rules + rid; +// if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0)) +// continue; /* only look at requires rules */ +// // solver_printrule(solv, SAT_DEBUG_RESULT, r); +// queue_empty(&q); +// queue_empty(&qi); +// havechoice = 0; +// FOR_RULELITERALS(p, pp, r) +// { +// if (p < 0) +// continue; +// s = pool->solvables + p; +// if (!s->repo) +// continue; +// if (s->repo == pool->installed) +// { +// queue_push(&q, p); +// continue; +// } +// /* check if this package is "blocked" by a installed package */ +// s2 = 0; +// FOR_PROVIDES(p2, pp2, s->name) +// { +// s2 = pool->solvables + p2; +// if (s2->repo != pool->installed) +// continue; +// if (!pool->implicitobsoleteusesprovides && s->name != s2->name) +// continue; +// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) +// continue; +// break; +// } +// if (p2) +// { +// /* found installed package p2 that we can update to p */ +// if (MAPTST(&mneg, p)) +// continue; +// if (policy_is_illegal(solv, s2, s, 0)) +// continue; +// queue_push(&qi, p2); +// queue_push(&q, p); +// continue; +// } +// if (s->obsoletes) +// { +// Id obs, *obsp = s->repo->idarraydata + s->obsoletes; +// s2 = 0; +// while ((obs = *obsp++) != 0) +// { +// FOR_PROVIDES(p2, pp2, obs) +// { +// s2 = pool->solvables + p2; +// if (s2->repo != pool->installed) +// continue; +// if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs)) +// continue; +// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) +// continue; +// break; +// } +// if (p2) +// break; +// } +// if (obs) +// { +// /* found installed package p2 that we can update to p */ +// if (MAPTST(&mneg, p)) +// continue; +// if (policy_is_illegal(solv, s2, s, 0)) +// continue; +// queue_push(&qi, p2); +// queue_push(&q, p); +// continue; +// } +// } +// /* package p is independent of the installed ones */ +// havechoice = 1; +// } +// if (!havechoice || !q.count) +// continue; /* no choice */ +// +// /* now check the update rules of the installed package. +// * if all packages of the update rules are contained in +// * the dependency rules, there's no need to set up the choice rule */ +// map_empty(&m); +// FOR_RULELITERALS(p, pp, r) +// if (p > 0) +// MAPSET(&m, p); +// for (i = 0; i < qi.count; i++) +// { +// if (!qi.elements[i]) +// continue; +// Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start); +// if (!ur->p) +// ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start); +// if (!ur->p) +// continue; +// FOR_RULELITERALS(p, pp, ur) +// if (!MAPTST(&m, p)) +// break; +// if (p) +// break; +// for (j = i + 1; j < qi.count; j++) +// if (qi.elements[i] == qi.elements[j]) +// qi.elements[j] = 0; +// } +// if (i == qi.count) +// { +// #if 0 +// printf("skipping choice "); +// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid); +// #endif +// continue; +// } +// d = q.count ? pool_queuetowhatprovides(pool, &q) : 0; +// solver_addrule(solv, r->p, d); +// queue_push(&solv->weakruleq, solv->nrules - 1); +// solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid; +// #if 0 +// printf("OLD "); +// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid); +// printf("WEAK CHOICE "); +// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1); +// #endif +// } +// queue_free(&q); +// queue_free(&qi); +// map_free(&m); +// map_free(&mneg); +// solv->choicerules_end = solv->nrules; +// } + } + +/*********************************************************************** + *** + *** Policy rule disabling/reenabling + *** + *** Disable all policy rules that conflict with our jobs. If a job + *** gets disabled later on, reenable the involved policy rules again. + *** + *** / + +#define DISABLE_UPDATE 1 +#define DISABLE_INFARCH 2 +#define DISABLE_DUP 3 +*/ + protected function jobToDisableQueue(array $job, array $disableQueue) + { + switch ($job['cmd']) { + case 'install': + foreach ($job['packages'] as $package) { + if (isset($this->installedMap[$package->getId()])) { + $disableQueue[] = array('type' => 'update', 'package' => $package); + } + + /* all job packages obsolete * / + qstart = q->count; + pass = 0; + memset(&omap, 0, sizeof(omap)); + FOR_JOB_SELECT(p, pp, select, what) + { + Id p2, pp2; + + if (pass == 1) + map_grow(&omap, installed->end - installed->start); + s = pool->solvables + p; + if (s->obsoletes) + { + Id obs, *obsp; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + FOR_PROVIDES(p2, pp2, obs) + { + Solvable *ps = pool->solvables + p2; + if (ps->repo != installed) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + if (pass) + MAPSET(&omap, p2 - installed->start); + else + queue_push2(q, DISABLE_UPDATE, p2); + } + } + FOR_PROVIDES(p2, pp2, s->name) + { + Solvable *ps = pool->solvables + p2; + if (ps->repo != installed) + continue; + if (!pool->implicitobsoleteusesprovides && ps->name != s->name) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + if (pass) + MAPSET(&omap, p2 - installed->start); + else + queue_push2(q, DISABLE_UPDATE, p2); + } + if (pass) + { + for (i = j = qstart; i < q->count; i += 2) + { + if (MAPTST(&omap, q->elements[i + 1] - installed->start)) + { + MAPCLR(&omap, q->elements[i + 1] - installed->start); + q->elements[j + 1] = q->elements[i + 1]; + j += 2; + } + } + queue_truncate(q, j); + } + if (q->count == qstart) + break; + pass++; + } + if (omap.size) + map_free(&omap); + + if (qstart == q->count) + return; /* nothing to prune * / + if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) + return; /* all is set */ + + /* now that we know which installed packages are obsoleted check each of them * / + for (i = j = qstart; i < q->count; i += 2) + { + Solvable *is = pool->solvables + q->elements[i + 1]; + FOR_JOB_SELECT(p, pp, select, what) + { + int illegal = 0; + s = pool->solvables + p; + if ((set & SOLVER_SETEVR) != 0) + illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * / + if ((set & SOLVER_SETARCH) != 0) + illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * / + if ((set & SOLVER_SETVENDOR) != 0) + illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * / + illegal = policy_is_illegal(solv, is, s, illegal); + if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0) + { + /* it's ok if the EV is different * / + if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0) + illegal = 0; + } + if (illegal) + break; + } + if (!p) + { + /* no package conflicts with the update rule * / + /* thus keep the DISABLE_UPDATE * / + q->elements[j + 1] = q->elements[i + 1]; + j += 2; + } + } + queue_truncate(q, j); + return;*/ + } + break; + + case 'remove': + foreach ($job['packages'] as $package) { + if (isset($this->installedMap[$package->getId()])) { + $disableQueue[] = array('type' => 'update', 'package' => $package); + } + } + break; + } + + return $disableQueue; + } + + protected function disableUpdateRule($package) + { + if (isset($this->packageToFeatureRule[$package->getId()])) { + $this->packageToFeatureRule[$package->getId()]->disable(); + } + } + + /** + * Disables all policy rules that conflict with jobs + */ + protected function disablePolicyRules() + { + $lastJob = null; + $allQueue = array(); + + $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB); + foreach ($iterator as $rule) { + if ($rule->isDisabled()) { + continue; + } + + $job = $this->ruleToJob[$rule->getId()]; + + if ($job === $lastJob) { + continue; + } + + $lastJob = $job; + + $allQueue = $this->jobToDisableQueue($job, $allQueue); + } + + foreach ($allQueue as $disable) { + switch ($disable['type']) { + case 'update': + $this->disableUpdateRule($disable['package']); + break; + default: + throw new \RuntimeException("Unsupported disable type: " . $disable['type']); + } } } public function solve(Request $request) { $this->jobs = $request->getJobs(); - - $this->setupInstalledMap(); + $installedPackages = $this->installed->getPackages(); + $this->installedMap = array(); + foreach ($installedPackages as $package) { + $this->installedMap[$package->getId()] = $package; + } if (version_compare(PHP_VERSION, '5.3.4', '>=')) { $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1); @@ -524,18 +876,18 @@ class Solver switch ($job['cmd']) { case 'update-all': - foreach ($this->installedMap as $package) { + foreach ($installedPackages as $package) { $this->updateMap[$package->getId()] = true; } break; } } - foreach ($this->installedMap as $package) { + foreach ($installedPackages as $package) { $this->addRulesForPackage($package); } - foreach ($this->installedMap as $package) { + foreach ($installedPackages as $package) { $this->addRulesForUpdatePackages($package); } @@ -551,11 +903,15 @@ class Solver } } - foreach ($this->installedMap as $package) { + // solver_addrpmrulesforweak(solv, &addedmap); + + foreach ($installedPackages as $package) { $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); - $this->packageToUpdateRule[$package->getId()] = $rule; + $rule->setWeak(true); + $this->addRule(RuleSet::TYPE_FEATURE, $rule); + $this->packageToFeatureRule[$package->getId()] = $rule; } foreach ($this->jobs as $job) { @@ -596,15 +952,23 @@ class Solver } } + $this->addChoiceRules(); + foreach ($this->rules as $rule) { $this->addWatchesToRule($rule); } + /* disable update rules that conflict with our job */ + $this->disablePolicyRules(); + /* make decisions based on job/update assertions */ $this->makeAssertionRuleDecisions(); $installRecommended = 0; $this->runSat(true, $installRecommended); + //$this->printDecisionMap(); + //findrecommendedsuggested(solv); + //solver_prepare_solutions(solv); if ($this->problems) { throw new SolverProblemsException($this->problems); @@ -625,8 +989,8 @@ class Solver if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) { $literals = array(); - if (isset($this->packageToUpdateRule[$package->getId()])) { - $literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals()); + if (isset($this->packageToFeatureRule[$package->getId()])) { + $literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals()); } foreach ($literals as $updateLiteral) { @@ -668,21 +1032,18 @@ class Solver } } - foreach ($this->decisionMap as $packageId => $decision) { - if ($packageId === 0) { - continue; - } - - if (0 == $decision && isset($this->installedMap[$packageId])) { - $transaction[] = new Operation\UninstallOperation( - $this->pool->packageById($packageId), null - ); - } - } - return array_reverse($transaction); } + protected $decisionQueue = array(); + protected $decisionQueueWhy = array(); + protected $decisionQueueFree = array(); + protected $propagateIndex; + protected $branches = array(); + protected $problems = array(); + protected $learnedPool = array(); + protected $recommendsIndex; + protected function literalFromId($id) { $package = $this->pool->packageById(abs($id)); @@ -1175,7 +1536,17 @@ class Solver $why = $lastWeakWhy; } + if ($lastWeakWhy->getType() == RuleSet::TYPE_CHOICE) { + $this->disableChoiceRules($lastWeakWhy); + } + $this->disableProblem($why); + + /** +@TODO what does v < 0 mean here? ($why == v) + if (v < 0) + solver_reenablepolicyrules(solv, -(v + 1)); +*/ $this->resetSolver(); return true; @@ -1266,7 +1637,9 @@ class Solver // * here's the main loop: // * 1) propagate new decisions (only needed once) // * 2) fulfill jobs + // * 3) try to keep installed packages // * 4) fulfill all unresolved rules + // * 5) install recommended packages // * 6) minimalize solution if we had choices // * if we encounter a problem, we rewind to a safe level and restart // * with step 1 @@ -1281,6 +1654,8 @@ class Solver $minimizationSteps = 0; $installedPos = 0; + $this->installedPackages = $this->installed->getPackages(); + while (true) { if (1 === $level) { @@ -1352,6 +1727,125 @@ class Solver } } + // handle installed packages + if ($level < $systemLevel) { + // use two passes if any packages are being updated + // -> better user experience + for ($pass = (count($this->updateMap)) ? 0 : 1; $pass < 2; $pass++) { + $passLevel = $level; + for ($i = $installedPos, $n = 0; $n < count($this->installedPackages); $i++, $n++) { + $repeat = false; + + if ($i == count($this->installedPackages)) { + $i = 0; + } + $literal = new Literal($this->installedPackages[$i], true); + + if ($this->decisionsContain($literal)) { + continue; + } + + // only process updates in first pass + /** TODO: && or || ? **/ + if (0 === $pass && !isset($this->updateMap[$literal->getPackageId()])) { + continue; + } + + $rule = null; + + if (isset($this->packageToFeatureRule[$literal->getPackageId()])) { + $rule = $this->packageToFeatureRule[$literal->getPackageId()]; + } + + if (!$rule || $rule->isDisabled()) { + continue; + } + + $updateRuleLiterals = $rule->getLiterals(); + + $decisionQueue = array(); + if (!isset($this->noUpdate[$literal->getPackageId()]) && ( + $this->decidedRemove($literal->getPackage()) || + isset($this->updateMap[$literal->getPackageId()]) || + !$literal->equals($updateRuleLiterals[0]) + )) { + foreach ($updateRuleLiterals as $ruleLiteral) { + if ($this->decidedInstall($ruleLiteral->getPackage())) { + // already fulfilled + $decisionQueue = array(); + break; + } + if ($this->undecided($ruleLiteral->getPackage())) { + $decisionQueue[] = $ruleLiteral; + } + } + } + + if (sizeof($decisionQueue)) { + $oLevel = $level; + $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule); + + if (0 === $level) { + return; + } + + if ($level <= $oLevel) { + $repeat = true; + } + } else if (!$repeat && $this->undecided($literal->getPackage())) { + // still undecided? keep package. + $oLevel = $level; + if (isset($this->cleanDepsMap[$literal->getPackageId()])) { + // clean deps removes package + $level = $this->setPropagateLearn($level, $literal->invert(), $disableRules, null); + } else { + // ckeeping package + $level = $this->setPropagateLearn($level, $literal, $disableRules, $rule); + } + + + if (0 === $level) { + return; + } + + if ($level <= $oLevel) { + $repeat = true; + } + } + + if ($repeat) { + if (1 === $level || $level < $passLevel) { + // trouble + break; + } + if ($level < $oLevel) { + // redo all + $n = 0; + } + + // repeat + $i--; + $n--; + continue; + } + } + + if ($n < count($this->installedPackages)) { + $installedPos = $i; // retry this problem next time + break; + } + + $installedPos = 0; + } + + $systemLevel = $level + 1; + + if ($pass < 2) { + // had trouble => retry + continue; + } + } + if ($level < $systemLevel) { $systemLevel = $level; } diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 3e15acf80..356934514 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -64,7 +64,7 @@ class FileDownloader implements DownloaderInterface $fileName = $this->getFileName($package, $path); - $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $processUrl = $this->processUrl($url); @@ -101,7 +101,6 @@ class FileDownloader implements DownloaderInterface */ public function remove(PackageInterface $package, $path) { - $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index cacc74fc4..2d19bba40 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -50,7 +50,7 @@ abstract class VcsDownloader implements DownloaderInterface throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information'); } - $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $this->filesystem->removeDirectory($path); $this->doDownload($package, $path); $this->io->write(''); @@ -65,7 +65,7 @@ abstract class VcsDownloader implements DownloaderInterface throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information'); } - $this->io->write(" - Installing " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); + $this->io->write(" - Package " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); $this->enforceCleanDirectory($path); $this->doUpdate($initial, $target, $path); $this->io->write(''); @@ -77,7 +77,6 @@ abstract class VcsDownloader implements DownloaderInterface public function remove(PackageInterface $package, $path) { $this->enforceCleanDirectory($path); - $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index d7f51badb..e4219c860 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -259,11 +259,9 @@ class Installer } } - // fix the version of all installed packages (+ platform) that are not - // in the current local repo to prevent rogue updates (e.g. non-dev - // updating when in dev) + // fix the version all installed packages that are not in the current local repo to prevent rogue updates foreach ($installedRepo->getPackages() as $package) { - if ($package->getRepository() === $localRepo) { + if ($package->getRepository() === $localRepo || $package->getRepository() instanceof PlatformRepository) { continue; } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 9be2e1d1d..0da923654 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -127,13 +127,6 @@ class LibraryInstaller implements InstallerInterface $this->downloadManager->remove($package, $downloadPath); $this->removeBinaries($package); $repo->removePackage($package); - - if (strpos($package->getName(), '/')) { - $packageVendorDir = dirname($downloadPath); - if (is_dir($packageVendorDir) && !glob($packageVendorDir.'/*')) { - @rmdir($packageVendorDir); - } - } } /** diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 56084f32a..28db18131 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -27,7 +27,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_LEARNED => array( + RuleSet::TYPE_FEATURE => array( new Rule(array(), 'update1', null), ), RuleSet::TYPE_PACKAGE => array(), @@ -46,7 +46,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( $this->rules[RuleSet::TYPE_JOB][0], $this->rules[RuleSet::TYPE_JOB][1], - $this->rules[RuleSet::TYPE_LEARNED][0], + $this->rules[RuleSet::TYPE_FEATURE][0], ); $this->assertEquals($expected, $result); @@ -64,7 +64,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( RuleSet::TYPE_JOB, RuleSet::TYPE_JOB, - RuleSet::TYPE_LEARNED, + RuleSet::TYPE_FEATURE, ); $this->assertEquals($expected, $result); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index f319651ae..54ad88a58 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -27,15 +27,17 @@ class RuleSetTest extends TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_LEARNED => array( + RuleSet::TYPE_FEATURE => array( new Rule(array(), 'update1', null), ), + RuleSet::TYPE_LEARNED => array(), + RuleSet::TYPE_CHOICE => array(), ); $ruleSet = new RuleSet; $ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB); - $ruleSet->add($rules[RuleSet::TYPE_LEARNED][0], RuleSet::TYPE_LEARNED); + $ruleSet->add($rules[RuleSet::TYPE_FEATURE][0], RuleSet::TYPE_FEATURE); $ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB); $this->assertEquals($rules, $ruleSet->getRules()); @@ -78,7 +80,7 @@ class RuleSetTest extends TestCase $rule1 = new Rule(array(), 'job1', null); $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); + $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); $iterator = $ruleSet->getIterator(); @@ -94,9 +96,9 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); + $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); - $iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_LEARNED); + $iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_FEATURE); $this->assertSame($rule2, $iterator->current()); } @@ -108,7 +110,7 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_LEARNED); + $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); $iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB); @@ -140,7 +142,7 @@ class RuleSetTest extends TestCase ->method('equal') ->will($this->returnValue(false)); - $ruleSet->add($rule, RuleSet::TYPE_LEARNED); + $ruleSet->add($rule, RuleSet::TYPE_FEATURE); $this->assertTrue($ruleSet->containsEqual($rule)); $this->assertFalse($ruleSet->containsEqual($rule2)); @@ -153,9 +155,9 @@ class RuleSetTest extends TestCase $literal = new Literal($this->getPackage('foo', '2.1'), true); $rule = new Rule(array($literal), 'job1', null); - $ruleSet->add($rule, RuleSet::TYPE_JOB); + $ruleSet->add($rule, RuleSet::TYPE_FEATURE); - $this->assertContains('JOB : (+foo-2.1.0.0)', $ruleSet->__toString()); + $this->assertContains('FEATURE : (+foo-2.1.0.0)', $ruleSet->__toString()); } private function getRuleMock() diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 2bc5a928a..49355a5a4 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -54,16 +54,6 @@ class SolverTest extends TestCase )); } - public function testSolverRemoveIfNotInstalled() - { - $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); - $this->reposComplete(); - - $this->checkSolverResult(array( - array('job' => 'remove', 'package' => $packageA), - )); - } - public function testInstallNonExistingPackageFails() { $this->repo->addPackage($this->getPackage('A', '1.0')); @@ -186,7 +176,6 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1')); $this->reposComplete(); - $this->request->install('A'); $this->request->update('A'); $this->checkSolverResult(array( @@ -202,7 +191,6 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $packageA->setRequires(array(new Link('A', 'B', null, 'requires'))); - $newPackageA->setRequires(array(new Link('A', 'B', null, 'requires'))); $this->reposComplete(); @@ -221,7 +209,6 @@ class SolverTest extends TestCase $this->repo->addPackage($this->getPackage('A', '1.0')); $this->reposComplete(); - $this->request->install('A'); $this->request->update('A'); $this->checkSolverResult(array()); @@ -236,8 +223,6 @@ class SolverTest extends TestCase $this->reposComplete(); - $this->request->install('A'); - $this->request->install('B'); $this->request->update('A'); $this->checkSolverResult(array( @@ -282,7 +267,7 @@ class SolverTest extends TestCase public function testSolverUpdateFullyConstrainedPrunesInstalledPackages() { $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); - $this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0')); + $this->repoInstalled->addPackage($this->getPackage('B', '1.0')); $this->repo->addPackage($newPackageA = $this->getPackage('A', '1.2')); $this->repo->addPackage($this->getPackage('A', '2.0')); $this->reposComplete(); @@ -290,15 +275,10 @@ class SolverTest extends TestCase $this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0')); $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0')); - $this->checkSolverResult(array( - array( - 'job' => 'remove', - 'package' => $packageB, - ), - array( - 'job' => 'update', - 'from' => $packageA, - 'to' => $newPackageA, + $this->checkSolverResult(array(array( + 'job' => 'update', + 'from' => $packageA, + 'to' => $newPackageA, ))); } @@ -317,7 +297,6 @@ class SolverTest extends TestCase $this->reposComplete(); $this->request->install('A'); - $this->request->install('C'); $this->request->update('C'); $this->request->remove('D'); From 2d4d9849295310ef8eef1e04d570e5a7dce0d7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Men=C5=BCyk?= Date: Tue, 1 May 2012 01:01:16 +0200 Subject: [PATCH 087/101] Fixed typo in the Installer --- 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 e4219c860..f27f687b8 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -110,7 +110,7 @@ class Installer * @param EventDispatcher $eventDispatcher * @param AutoloadGenerator $autoloadGenerator */ - public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, autoloadGenerator $autoloadGenerator) + public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, AutoloadGenerator $autoloadGenerator) { $this->io = $io; $this->package = $package; From efae2c8829e6f0ae59dacf6554e44b59a9af460b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 1 May 2012 11:45:01 +0200 Subject: [PATCH 088/101] Alias root packages added in a4c2337 need to report composer.json alias info --- src/Composer/Package/AliasPackage.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index aee9a65ed..6670b93e5 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -244,6 +244,14 @@ class AliasPackage extends BasePackage { return $this->aliasOf->getScripts(); } + public function setAliases(array $aliases) + { + return $this->aliasOf->setAliases($aliases); + } + public function getAliases() + { + return $this->aliasOf->getAliases(); + } public function getLicense() { return $this->aliasOf->getLicense(); From 0923025455cf729e73425ac26f279eac22b096e1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 1 May 2012 13:10:14 +0200 Subject: [PATCH 089/101] Fix for when the version is explicitely set in the composer.json file --- src/Composer/Package/Loader/RootPackageLoader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 517e44e51..c38a205e9 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -54,6 +54,8 @@ class RootPackageLoader extends ArrayLoader } $config['version'] = $version; + } else { + $version = $config['version']; } $package = parent::load($config); From 0edc94cdcefbe9164038659afb0cadccc601dae6 Mon Sep 17 00:00:00 2001 From: Michael Cullum Date: Sat, 28 Apr 2012 15:26:47 +0100 Subject: [PATCH 090/101] Adding role to author information --- doc/04-schema.md | 7 +++++-- res/composer-schema.json | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index b9552061e..fb7ef7376 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -145,6 +145,7 @@ Each author object can have following properties: * **name:** The author's name. Usually his real name. * **email:** The author's email address. * **homepage:** An URL to the author's website. +* **role:** The authors role in the project (for e.g. developer or translator) An example: @@ -153,12 +154,14 @@ An example: { "name": "Nils Adermann", "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "homepage": "http://www.naderman.de", + "role": "Developer" }, { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "http://seld.be", + "role": "Developer" } ] } diff --git a/res/composer-schema.json b/res/composer-schema.json index c11ed953e..1d1ddfadf 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -66,6 +66,10 @@ "type": "string", "description": "Homepage URL for the author.", "format": "uri" + }, + "role": { + "type": "string", + "description": "Author's Role in development." } } } From 2653ece0285753f0ffd16566262e8594b9d204c5 Mon Sep 17 00:00:00 2001 From: Michael Cullum Date: Sun, 29 Apr 2012 21:55:09 +0100 Subject: [PATCH 091/101] Adding author role to ArrayLoader Test --- tests/Composer/Test/Package/Loader/ArrayLoaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 0bee2bf1a..508936d2d 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -89,7 +89,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase 'homepage' => 'http://example.com', 'license' => array('MIT', 'GPLv3'), 'authors' => array( - array('name' => 'Bob', 'email' => 'bob@example.org', 'homepage' => 'example.org'), + array('name' => 'Bob', 'email' => 'bob@example.org', 'homepage' => 'example.org', 'role' => 'Developer'), ), 'require' => array( 'foo/bar' => '1.0', From 14f7d10478a2316802ee826dd8e2a15636546149 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 1 May 2012 16:01:55 +0200 Subject: [PATCH 092/101] Fix typos --- doc/04-schema.md | 2 +- res/composer-schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index fb7ef7376..fe91cf4ed 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -145,7 +145,7 @@ Each author object can have following properties: * **name:** The author's name. Usually his real name. * **email:** The author's email address. * **homepage:** An URL to the author's website. -* **role:** The authors role in the project (for e.g. developer or translator) +* **role:** The authors' role in the project (e.g. developer or translator) An example: diff --git a/res/composer-schema.json b/res/composer-schema.json index 1d1ddfadf..f1f7ed1f9 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -69,7 +69,7 @@ }, "role": { "type": "string", - "description": "Author's Role in development." + "description": "Author's role in the project." } } } From 419a1cedf0ebcf190b7c27634eb87db856c87192 Mon Sep 17 00:00:00 2001 From: Maxim Chernyshev Date: Wed, 2 May 2012 08:26:27 +0800 Subject: [PATCH 093/101] Proxy credentials are now passed as part of HTTP headers --- src/Composer/Util/StreamContextFactory.php | 38 ++++++++++++++++--- .../Test/Util/StreamContextFactoryTest.php | 13 +++---- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 6541d811b..9826628e3 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -35,20 +35,48 @@ final class StreamContextFactory if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) { // Some systems seem to rely on a lowercased version instead... $proxy = isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']; - + + $proxyURL = parse_url($proxy, PHP_URL_SCHEME) . "://"; + $proxyURL .= parse_url($proxy, PHP_URL_HOST); + + $proxyPort = parse_url($proxy, PHP_URL_PORT); + + if (isset($proxyPort)) { + $proxyURL .= ":" . $proxyPort; + } + // http(s):// is not supported in proxy - $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxy); + $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL); - if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { + if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) { throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); } $options['http'] = array( - 'proxy' => $proxy, + 'proxy' => $proxyURL, 'request_fulluri' => true, ); + + // Extract authentication credentials from the proxy url + $user = parse_url($proxy, PHP_URL_USER); + $pass = parse_url($proxy, PHP_URL_PASS); + + if (isset($user)) { + $auth = $user; + if (isset($pass)) { + $auth .= ":{$pass}"; + } + $auth = base64_encode($auth); + + // Preserve headers if already set in default options + if (isset($defaultOptions['http']) && isset($defaultOptions['http']['header'])) { + $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; + } else { + $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n"; + } + } } - + $options = array_merge_recursive($options, $defaultOptions); return stream_context_create($options, $defaultParams); diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 8429d078b..0e9928ede 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -57,31 +57,30 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase public function testHttpProxy() { - $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:port/'; - $_SERVER['HTTP_PROXY'] = 'http://proxyserver/'; + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:1234'; + $_SERVER['HTTP_PROXY'] = 'http://proxyserver'; $context = StreamContextFactory::getContext(array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); - $this->assertSame('http://proxyserver/', $_SERVER['HTTP_PROXY']); - $this->assertEquals(array('http' => array( - 'proxy' => 'tcp://username:password@proxyserver.net:port/', + 'proxy' => 'tcp://proxyserver.net:1234', 'request_fulluri' => true, 'method' => 'GET', + 'header' => "Proxy-Authorization: Basic " . base64_encode('username:password') . "\r\n" )), $options); } public function testSSLProxy() { - $_SERVER['http_proxy'] = 'https://proxyserver/'; + $_SERVER['http_proxy'] = 'https://proxyserver'; if (extension_loaded('openssl')) { $context = StreamContextFactory::getContext(); $options = stream_context_get_options($context); $this->assertSame(array('http' => array( - 'proxy' => 'ssl://proxyserver/', + 'proxy' => 'ssl://proxyserver', 'request_fulluri' => true, )), $options); } else { From 8918551f297ef28f407646a225e02477f253d186 Mon Sep 17 00:00:00 2001 From: Maxim Chernyshev Date: Wed, 2 May 2012 09:22:59 +0800 Subject: [PATCH 094/101] Simplified proxy port determination and passed tests --- src/Composer/Util/StreamContextFactory.php | 14 ++++++++------ .../Test/Util/StreamContextFactoryTest.php | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 384ff79dd..04561670e 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -43,17 +43,19 @@ final class StreamContextFactory if (isset($proxyPort)) { $proxyURL .= ":" . $proxyPort; + } else { + if ('http://' == substr($proxyURL, 0, 7)) { + $proxyURL .= ":80"; + } else if ('https://' == substr($proxyURL, 0, 8)) { + $proxyURL .= ":443"; + } } // http(s):// is not supported in proxy - if ('http://' == substr($proxy, 0, 7)) { - $proxy = 'tcp://' . rtrim(substr($proxy, 7), '/') . (parse_url($proxy, PHP_URL_PORT) ? '' : ':80'); - } else if ('https://' == substr($proxy, 0, 8)) { - $proxy = 'ssl://' . rtrim(substr($proxy, 8), '/') . (parse_url($proxy, PHP_URL_PORT) ? '' : ':443'); - } + $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL); if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) { - throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); + throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); } $options['http'] = array( diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index b50c0cef8..d39fbe340 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -97,7 +97,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase $context = StreamContextFactory::getContext(); $options = stream_context_get_options($context); - $this->assertEquals(array('http' => array( + $this->assertEquals(array('http' => array( 'proxy' => $expected, 'request_fulluri' => true, )), $options); From 37476eb61f2cba5a557e2200da67dddf683a3ed4 Mon Sep 17 00:00:00 2001 From: Maxim Chernyshev Date: Wed, 2 May 2012 09:30:27 +0800 Subject: [PATCH 095/101] Fixed indentation --- src/Composer/Util/StreamContextFactory.php | 38 +++++++++---------- .../Test/Util/StreamContextFactoryTest.php | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 04561670e..0cc591484 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -42,20 +42,20 @@ final class StreamContextFactory $proxyPort = parse_url($proxy, PHP_URL_PORT); if (isset($proxyPort)) { - $proxyURL .= ":" . $proxyPort; + $proxyURL .= ":" . $proxyPort; } else { - if ('http://' == substr($proxyURL, 0, 7)) { - $proxyURL .= ":80"; - } else if ('https://' == substr($proxyURL, 0, 8)) { - $proxyURL .= ":443"; - } + if ('http://' == substr($proxyURL, 0, 7)) { + $proxyURL .= ":80"; + } else if ('https://' == substr($proxyURL, 0, 8)) { + $proxyURL .= ":443"; + } } // http(s):// is not supported in proxy $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL); if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) { - throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); + throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); } $options['http'] = array( @@ -68,18 +68,18 @@ final class StreamContextFactory $pass = parse_url($proxy, PHP_URL_PASS); if (isset($user)) { - $auth = $user; - if (isset($pass)) { - $auth .= ":{$pass}"; - } - $auth = base64_encode($auth); - - // Preserve headers if already set in default options - if (isset($defaultOptions['http']) && isset($defaultOptions['http']['header'])) { - $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; - } else { - $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n"; - } + $auth = $user; + if (isset($pass)) { + $auth .= ":{$pass}"; + } + $auth = base64_encode($auth); + + // Preserve headers if already set in default options + if (isset($defaultOptions['http']) && isset($defaultOptions['http']['header'])) { + $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; + } else { + $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n"; + } } } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index d39fbe340..7fa74e0d9 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -96,8 +96,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase if (extension_loaded('openssl')) { $context = StreamContextFactory::getContext(); $options = stream_context_get_options($context); - - $this->assertEquals(array('http' => array( + + $this->assertEquals(array('http' => array( 'proxy' => $expected, 'request_fulluri' => true, )), $options); From 1ae0a1b7af21b7b07db0ec952b8599ba84915d9e Mon Sep 17 00:00:00 2001 From: Maxim Chernyshev Date: Wed, 2 May 2012 15:16:36 +0800 Subject: [PATCH 096/101] Follow-up coding standard fixes --- src/Composer/Util/StreamContextFactory.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 0cc591484..c0e6a5f25 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -43,12 +43,10 @@ final class StreamContextFactory if (isset($proxyPort)) { $proxyURL .= ":" . $proxyPort; - } else { - if ('http://' == substr($proxyURL, 0, 7)) { - $proxyURL .= ":80"; - } else if ('https://' == substr($proxyURL, 0, 8)) { - $proxyURL .= ":443"; - } + } else if ('http://' == substr($proxyURL, 0, 7)) { + $proxyURL .= ":80"; + } else if ('https://' == substr($proxyURL, 0, 8)) { + $proxyURL .= ":443"; } // http(s):// is not supported in proxy @@ -75,14 +73,13 @@ final class StreamContextFactory $auth = base64_encode($auth); // Preserve headers if already set in default options - if (isset($defaultOptions['http']) && isset($defaultOptions['http']['header'])) { + if (isset($defaultOptions['http']['header'])) { $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; } else { $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n"; } } } - $options = array_merge_recursive($options, $defaultOptions); return stream_context_create($options, $defaultParams); From 5294cb222cd7b8cae17ac6a98a70534410eb8b12 Mon Sep 17 00:00:00 2001 From: Maxim Chernyshev Date: Wed, 2 May 2012 17:21:58 +0800 Subject: [PATCH 097/101] Precious community feedback-based refactoring --- src/Composer/Util/StreamContextFactory.php | 45 +++++++++---------- .../Test/Util/StreamContextFactoryTest.php | 2 +- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index c0e6a5f25..df61b3c3e 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -34,21 +34,23 @@ final class StreamContextFactory // Handle system proxy if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) { // Some systems seem to rely on a lowercased version instead... - $proxy = isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']; - - $proxyURL = parse_url($proxy, PHP_URL_SCHEME) . "://"; - $proxyURL .= parse_url($proxy, PHP_URL_HOST); - - $proxyPort = parse_url($proxy, PHP_URL_PORT); - - if (isset($proxyPort)) { - $proxyURL .= ":" . $proxyPort; - } else if ('http://' == substr($proxyURL, 0, 7)) { + $proxy = parse_url(isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']); + } else { + $proxy = false; + } + + if (false !== $proxy) { + $proxyURL = (isset($proxy['scheme']) ? $proxy['scheme'] : '') . '://'; + $proxyURL .= isset($proxy['host']) ? $proxy['host'] : ''; + + if (isset($proxy['port'])) { + $proxyURL .= ":" . $proxy['port']; + } elseif ('http://' == substr($proxyURL, 0, 7)) { $proxyURL .= ":80"; - } else if ('https://' == substr($proxyURL, 0, 8)) { + } elseif ('https://' == substr($proxyURL, 0, 8)) { $proxyURL .= ":443"; } - + // http(s):// is not supported in proxy $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL); @@ -60,18 +62,14 @@ final class StreamContextFactory 'proxy' => $proxyURL, 'request_fulluri' => true, ); - - // Extract authentication credentials from the proxy url - $user = parse_url($proxy, PHP_URL_USER); - $pass = parse_url($proxy, PHP_URL_PASS); - - if (isset($user)) { - $auth = $user; - if (isset($pass)) { - $auth .= ":{$pass}"; + + if (isset($proxy['user'])) { + $auth = $proxy['user']; + if (isset($proxy['pass'])) { + $auth .= ':' . $proxy['pass']; } $auth = base64_encode($auth); - + // Preserve headers if already set in default options if (isset($defaultOptions['http']['header'])) { $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; @@ -80,8 +78,9 @@ final class StreamContextFactory } } } + $options = array_merge_recursive($options, $defaultOptions); - + return stream_context_create($options, $defaultParams); } } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 7fa74e0d9..dc60fe3bb 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -96,7 +96,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase if (extension_loaded('openssl')) { $context = StreamContextFactory::getContext(); $options = stream_context_get_options($context); - + $this->assertEquals(array('http' => array( 'proxy' => $expected, 'request_fulluri' => true, From 28941f169b61defdb58a977550b4058e7636f2c5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 2 May 2012 15:22:26 +0200 Subject: [PATCH 098/101] Fix docs --- doc/02-libraries.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 81d665fda..1b6a6a8a6 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -62,16 +62,17 @@ Here are a few examples of valid tag names: For every branch, a package development version will be created. If the branch name looks like a version, the version will be `{branchname}-dev`. For example -a branch `2.0` will get a version `2.0-dev`. If the branch does not look like -a version, it will be `dev-{branchname}`. `master` results in a `dev-master` -version. +a branch `2.0` will get a version `2.0.x-dev` (the `.x` is added for technical +reasons, to make sure it is recognized as a branch, a `2.0.x` branch would also +be valid and be turned into `2.0.x-dev` as well. If the branch does not look +like a version, it will be `dev-{branchname}`. `master` results in a +`dev-master` version. Here are some examples of version branch names: - 1.0 - 1.* + 1.x + 1.0 (equals 1.0.x) 1.1.x - 1.1.* > **Note:** When you install a dev version, it will install it from source. See [Repositories](05-repositories.md) for more information. @@ -79,7 +80,7 @@ See [Repositories](05-repositories.md) for more information. ### Aliases It is possible alias branch names to versions. For example, you could alias -`dev-master` to `1.0-dev`, which would allow you to require `1.0-dev` in all +`dev-master` to `1.0.x-dev`, which would allow you to require `1.0.x-dev` in all the packages. See [Aliases](articles/aliases.md) for more information. From 133f3317eede72fb4df33ef0ef5c9b7e22c088e8 Mon Sep 17 00:00:00 2001 From: Daniel Mecke Date: Fri, 4 May 2012 14:30:42 +0300 Subject: [PATCH 099/101] fixed typo --- 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 91ee3e66b..b4fd4699e 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -67,7 +67,7 @@ executable and invoke it without `php`. ### Using Composer -Next, run the command the `install` command to resolve and download dependencies: +Next, run the `install` command to resolve and download dependencies: $ php composer.phar install From c454d93a20367872e1be8e83a5e32855d26786b2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 6 May 2012 13:28:37 +0200 Subject: [PATCH 100/101] Move all logic within try/catch to prevent an incorrect version from blowing up the whole package --- src/Composer/Repository/VcsRepository.php | 94 +++++++++++------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index ecc435582..ea560fc92 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -34,7 +34,7 @@ class VcsRepository extends ArrayRepository protected $versionParser; protected $type; - public function __construct(array $repoConfig, IOInterface $io, Config $config = null, array $drivers = null) + public function __construct(array $repoConfig, IOInterface $io, Config $config, array $drivers = null) { $this->drivers = $drivers ?: array( 'github' => 'Composer\Repository\Vcs\GitHubDriver', @@ -104,7 +104,7 @@ class VcsRepository extends ArrayRepository } foreach ($driver->getTags() as $tag => $identifier) { - $msg = 'Get composer info for ' . ($this->packageName ?: $this->url) . ' (' . $tag . ')'; + $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $tag . ')'; if ($verbose) { $this->io->write($msg); } else { @@ -125,45 +125,45 @@ class VcsRepository extends ArrayRepository } continue; } + + // manually versioned package + if (isset($data['version'])) { + $data['version_normalized'] = $this->versionParser->normalize($data['version']); + } else { + // auto-versionned package, read value from tag + $data['version'] = $tag; + $data['version_normalized'] = $parsedTag; + } + + // make sure tag packages have no -dev flag + $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']); + $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); + + // broken package, version doesn't match tag + if ($data['version_normalized'] !== $parsedTag) { + if ($verbose) { + $this->io->write('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); + } + continue; + } + + if ($verbose) { + $this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')'); + } + + $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } catch (\Exception $e) { if ($verbose) { $this->io->write('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage())); } continue; } - - // manually versioned package - if (isset($data['version'])) { - $data['version_normalized'] = $this->versionParser->normalize($data['version']); - } else { - // auto-versionned package, read value from tag - $data['version'] = $tag; - $data['version_normalized'] = $parsedTag; - } - - // make sure tag packages have no -dev flag - $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']); - $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); - - // broken package, version doesn't match tag - if ($data['version_normalized'] !== $parsedTag) { - if ($verbose) { - $this->io->write('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); - } - continue; - } - - if ($verbose) { - $this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')'); - } - - $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } $this->io->overwrite('', false); foreach ($driver->getBranches() as $branch => $identifier) { - $msg = 'Get composer info for ' . ($this->packageName ?: $this->url) . ' (' . $branch . ')'; + $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $branch . ')'; if ($verbose) { $this->io->write($msg); } else { @@ -184,6 +184,23 @@ class VcsRepository extends ArrayRepository } continue; } + + // branches are always auto-versionned, read value from branch name + $data['version'] = $branch; + $data['version_normalized'] = $parsedBranch; + + // make sure branch packages have a dev flag + if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { + $data['version'] = 'dev-' . $data['version']; + } else { + $data['version'] = preg_replace('{(\.9{7})+}', '.x', $parsedBranch); + } + + if ($verbose) { + $this->io->write('Importing branch '.$branch.' ('.$data['version'].')'); + } + + $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } catch (TransportException $e) { if ($verbose) { $this->io->write('Skipped branch '.$branch.', no composer file was found'); @@ -193,23 +210,6 @@ class VcsRepository extends ArrayRepository $this->io->write('Skipped branch '.$branch.', '.$e->getMessage()); continue; } - - // branches are always auto-versionned, read value from branch name - $data['version'] = $branch; - $data['version_normalized'] = $parsedBranch; - - // make sure branch packages have a dev flag - if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { - $data['version'] = 'dev-' . $data['version']; - } else { - $data['version'] = preg_replace('{(\.9{7})+}', '.x', $parsedBranch); - } - - if ($verbose) { - $this->io->write('Importing branch '.$branch.' ('.$data['version'].')'); - } - - $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } $this->io->overwrite('', false); From 072f4397a016516d17de07f324712966a5c36091 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 6 May 2012 13:50:18 +0200 Subject: [PATCH 101/101] Fix default options handling in StreamContextFactory --- src/Composer/Util/StreamContextFactory.php | 10 ++++------ .../Test/Util/StreamContextFactoryTest.php | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index df61b3c3e..8860e7f17 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -35,11 +35,9 @@ final class StreamContextFactory if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) { // Some systems seem to rely on a lowercased version instead... $proxy = parse_url(isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']); - } else { - $proxy = false; } - if (false !== $proxy) { + if (!empty($proxy)) { $proxyURL = (isset($proxy['scheme']) ? $proxy['scheme'] : '') . '://'; $proxyURL .= isset($proxy['host']) ? $proxy['host'] : ''; @@ -70,16 +68,16 @@ final class StreamContextFactory } $auth = base64_encode($auth); - // Preserve headers if already set in default options + // Preserve headers if already set in default options if (isset($defaultOptions['http']['header'])) { - $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; + $defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n"; } else { $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n"; } } } - $options = array_merge_recursive($options, $defaultOptions); + $options = array_replace_recursive($options, $defaultOptions); return stream_context_create($options, $defaultParams); } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index dc60fe3bb..691625a94 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -71,6 +71,21 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase )), $options); } + public function testOptionsArePreserved() + { + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:3128/'; + + $context = StreamContextFactory::getContext(array('http' => array('method' => 'GET', 'header' => "X-Foo: bar\r\n", 'request_fulluri' => false))); + $options = stream_context_get_options($context); + + $this->assertEquals(array('http' => array( + 'proxy' => 'tcp://proxyserver.net:3128', + 'request_fulluri' => false, + 'method' => 'GET', + 'header' => "X-Foo: bar\r\nProxy-Authorization: Basic " . base64_encode('username:password') . "\r\n" + )), $options); + } + public function testHttpProxyWithoutPort() { $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net';