Procházet zdrojové kódy

Merge remote-tracking branch 'parent/master'

Conflicts:
	src/Composer/Factory.php
Nicolas Toniazzi před 9 roky
rodič
revize
b6df8549cd
100 změnil soubory, kde provedl 1930 přidání a 2577 odebrání
  1. 26 3
      .php_cs
  2. 3 2
      .travis.yml
  3. 5 3
      CONTRIBUTING.md
  4. 2 3
      README.md
  5. 33 0
      appveyor.yml
  6. 0 9
      bin/update-spdx-licenses
  7. 5 2
      composer.json
  8. 279 113
      composer.lock
  9. 67 86
      doc/00-intro.md
  10. 79 115
      doc/01-basic-usage.md
  11. 39 37
      doc/02-libraries.md
  12. 41 8
      doc/03-cli.md
  13. 40 0
      doc/05-repositories.md
  14. 10 11
      doc/06-config.md
  15. 1 1
      doc/articles/handling-private-packages-with-satis.md
  16. 27 12
      doc/articles/scripts.md
  17. 36 7
      doc/articles/troubleshooting.md
  18. 118 0
      doc/articles/versions.md
  19. 0 1226
      res/spdx-licenses.json
  20. 84 23
      src/Composer/Autoload/AutoloadGenerator.php
  21. 4 4
      src/Composer/Autoload/ClassLoader.php
  22. 2 3
      src/Composer/Autoload/ClassMapGenerator.php
  23. 9 4
      src/Composer/Cache.php
  24. 7 2
      src/Composer/Command/ArchiveCommand.php
  25. 25 28
      src/Composer/Command/ConfigCommand.php
  26. 8 7
      src/Composer/Command/CreateProjectCommand.php
  27. 3 2
      src/Composer/Command/DependsCommand.php
  28. 27 25
      src/Composer/Command/DiagnoseCommand.php
  29. 5 2
      src/Composer/Command/DumpAutoloadCommand.php
  30. 3 3
      src/Composer/Command/HomeCommand.php
  31. 33 30
      src/Composer/Command/InitCommand.php
  32. 8 5
      src/Composer/Command/InstallCommand.php
  33. 10 12
      src/Composer/Command/LicensesCommand.php
  34. 13 6
      src/Composer/Command/RemoveCommand.php
  35. 14 8
      src/Composer/Command/RequireCommand.php
  36. 3 2
      src/Composer/Command/RunScriptCommand.php
  37. 4 3
      src/Composer/Command/SearchCommand.php
  38. 14 12
      src/Composer/Command/SelfUpdateCommand.php
  39. 41 37
      src/Composer/Command/ShowCommand.php
  40. 11 6
      src/Composer/Command/StatusCommand.php
  41. 98 0
      src/Composer/Command/SuggestsCommand.php
  42. 7 4
      src/Composer/Command/UpdateCommand.php
  43. 77 26
      src/Composer/Command/ValidateCommand.php
  44. 6 2
      src/Composer/Compiler.php
  45. 5 6
      src/Composer/Config.php
  46. 17 13
      src/Composer/Console/Application.php
  47. 3 2
      src/Composer/Console/HtmlOutputFormatter.php
  48. 4 4
      src/Composer/DependencyResolver/DefaultPolicy.php
  49. 1 0
      src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php
  50. 1 0
      src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php
  51. 1 2
      src/Composer/DependencyResolver/Operation/SolverOperation.php
  52. 31 23
      src/Composer/DependencyResolver/Pool.php
  53. 7 3
      src/Composer/DependencyResolver/Problem.php
  54. 7 7
      src/Composer/DependencyResolver/Request.php
  55. 27 42
      src/Composer/DependencyResolver/Rule.php
  56. 2 3
      src/Composer/DependencyResolver/RuleSet.php
  57. 1 1
      src/Composer/DependencyResolver/RuleWatchNode.php
  58. 9 4
      src/Composer/DependencyResolver/Solver.php
  59. 1 1
      src/Composer/DependencyResolver/SolverProblemsException.php
  60. 6 2
      src/Composer/Downloader/ArchiveDownloader.php
  61. 5 7
      src/Composer/Downloader/DownloadManager.php
  62. 6 8
      src/Composer/Downloader/FileDownloader.php
  63. 26 9
      src/Composer/Downloader/GitDownloader.php
  64. 61 0
      src/Composer/Downloader/PathDownloader.php
  65. 1 2
      src/Composer/Downloader/PearPackageExtractor.php
  66. 1 1
      src/Composer/Downloader/PerforceDownloader.php
  67. 1 1
      src/Composer/Downloader/SvnDownloader.php
  68. 3 4
      src/Composer/Downloader/VcsDownloader.php
  69. 2 2
      src/Composer/EventDispatcher/Event.php
  70. 24 11
      src/Composer/EventDispatcher/EventDispatcher.php
  71. 7 3
      src/Composer/Factory.php
  72. 2 2
      src/Composer/IO/BufferIO.php
  73. 5 6
      src/Composer/IO/ConsoleIO.php
  74. 6 8
      src/Composer/IO/IOInterface.php
  75. 133 29
      src/Composer/Installer.php
  76. 3 4
      src/Composer/Installer/InstallationManager.php
  77. 1 0
      src/Composer/Installer/InstallerInterface.php
  78. 5 0
      src/Composer/Installer/LibraryInstaller.php
  79. 0 1
      src/Composer/Installer/PackageEvent.php
  80. 0 2
      src/Composer/Installer/PluginInstaller.php
  81. 3 3
      src/Composer/Json/JsonFile.php
  82. 0 1
      src/Composer/Json/JsonFormatter.php
  83. 5 5
      src/Composer/Package/AliasPackage.php
  84. 3 4
      src/Composer/Package/Archiver/ArchivableFilesFinder.php
  85. 1 1
      src/Composer/Package/Archiver/ArchiverInterface.php
  86. 2 2
      src/Composer/Package/Archiver/BaseExcludeFilter.php
  87. 1 1
      src/Composer/Package/Archiver/HgExcludeFilter.php
  88. 18 1
      src/Composer/Package/BasePackage.php
  89. 2 2
      src/Composer/Package/CompletePackage.php
  90. 1 1
      src/Composer/Package/CompletePackageInterface.php
  91. 10 10
      src/Composer/Package/Link.php
  92. 6 29
      src/Composer/Package/LinkConstraint/EmptyConstraint.php
  93. 6 8
      src/Composer/Package/LinkConstraint/LinkConstraintInterface.php
  94. 6 65
      src/Composer/Package/LinkConstraint/MultiConstraint.php
  95. 6 35
      src/Composer/Package/LinkConstraint/SpecificConstraint.php
  96. 6 106
      src/Composer/Package/LinkConstraint/VersionConstraint.php
  97. 27 3
      src/Composer/Package/Loader/ArrayLoader.php
  98. 19 178
      src/Composer/Package/Loader/RootPackageLoader.php
  99. 3 3
      src/Composer/Package/Loader/ValidatingArrayLoader.php
  100. 63 12
      src/Composer/Package/Locker.php

+ 26 - 3
.php_cs

@@ -10,9 +10,32 @@ $finder = Symfony\CS\Finder\DefaultFinder::create()
 
 return Symfony\CS\Config\Config::create()
     ->setUsingCache(true)
-    ->level(Symfony\CS\FixerInterface::NONE_LEVEL)
-    ->fixers(array(
-        'psr0', 'encoding', 'short_tag', 'braces', 'elseif', 'eof_ending', 'function_declaration', 'indentation', 'line_after_namespace', 'linefeed', 'lowercase_constants', 'lowercase_keywords', 'multiple_use', 'php_closing_tag', 'trailing_spaces', 'visibility', 'duplicate_semicolon', 'extra_empty_lines', 'include', 'namespace_no_leading_whitespace', 'object_operator', 'operators_spaces', 'phpdoc_params', 'return', 'single_array_no_trailing_comma', 'spaces_cast', 'standardize_not_equal', 'ternary_spaces', 'unused_use', 'whitespacy_lines', 'multiline_array_tailing_comma',
+    ->setRiskyAllowed(true)
+    ->setRules(array(
+        '@PSR2' => true,
+        'duplicate_semicolon' => true,
+        'extra_empty_lines' => true,
+        'include' => true,
+        'multiline_array_trailing_comma' => true,
+        'namespace_no_leading_whitespace' => true,
+        'object_operator' => true,
+        'operators_spaces' => true,
+        'phpdoc_align' => true,
+        'phpdoc_indent' => true,
+        'phpdoc_no_access' => true,
+        'phpdoc_no_package' => true,
+        'phpdoc_order' => true,
+        'phpdoc_scalar' => true,
+        'phpdoc_trim' => true,
+        'phpdoc_type_to_var' => true,
+        'psr0' => true,
+        'return' => true,
+        'single_array_no_trailing_comma' => true,
+        'spaces_cast' => true,
+        'standardize_not_equal' => true,
+        'ternary_spaces' => true,
+        'unused_use' => true,
+        'whitespacy_lines' => true,
     ))
     ->finder($finder)
 ;

+ 3 - 2
.travis.yml

@@ -7,8 +7,9 @@ cache:
     - $HOME/.composer/cache
 
 addons:
-  apt_packages:
-    - parallel
+  apt:
+    packages:
+      - parallel
 
 php:
   - 5.3.3

+ 5 - 3
CONTRIBUTING.md

@@ -2,7 +2,7 @@ Contributing to Composer
 ========================
 
 Please note that this project is released with a
-[Contributor Code of Conduct](http://contributor-covenant.org/version/1/0/0/).
+[Contributor Code of Conduct](http://contributor-covenant.org/version/1/2/0/).
 By participating in this project you agree to abide by its terms.
 
 Reporting Issues
@@ -42,6 +42,8 @@ Contributing policy
 Fork the project, create a feature branch, and send us a pull request.
 
 To ensure a consistent code base, you should make sure the code follows
-the [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/).
+the [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/). You can also
+run [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) with the
+configuration file that can be found in the project root directory.
 
-If you would like to help, take a look at the [list of issues](https://github.com/composer/composer/issues).
+If you would like to help, take a look at the [list of open issues](https://github.com/composer/composer/issues).

+ 2 - 3
README.md

@@ -6,7 +6,7 @@ Composer helps you declare, manage and install dependencies of PHP projects, ens
 See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation.
 
 [![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer)
-[![Dependency Status](https://www.versioneye.com/php/composer:composer/dev-master/badge.svg)](https://www.versioneye.com/php/composer:composer/dev-master) 
+[![Dependency Status](https://www.versioneye.com/php/composer:composer/dev-master/badge.svg)](https://www.versioneye.com/php/composer:composer/dev-master)
 [![Reference Status](https://www.versioneye.com/php/composer:composer/reference_badge.svg?style=flat)](https://www.versioneye.com/php/composer:composer/references)
 
 Installation / Usage
@@ -45,7 +45,6 @@ Updating Composer
 Running `php composer.phar self-update` or equivalent will update a phar
 install to the latest version.
 
-
 Community
 ---------
 
@@ -56,7 +55,7 @@ For support, Stack Overflow also offers a good collection of
 [Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
 
 Please note that this project is released with a
-[Contributor Code of Conduct](http://contributor-covenant.org/version/1/0/0/).
+[Contributor Code of Conduct](http://contributor-covenant.org/version/1/2/0/).
 By participating in this project and its community you agree to abide by those terms.
 
 Requirements

+ 33 - 0
appveyor.yml

@@ -0,0 +1,33 @@
+build: false
+shallow_clone: true
+platform: x86
+clone_folder: c:\projects\composer
+
+cache:
+    - c:\tools\php -> appveyor.yml
+
+init:
+    - SET PATH=C:\Program Files\OpenSSL;c:\tools\php;%PATH%
+    - SET COMPOSER_NO_INTERACTION=1
+    - SET PHP=1
+    - SET ANSICON=121x90 (121x90)
+
+install:
+    - IF EXIST c:\tools\php (SET PHP=0)
+    - IF %PHP%==1 cinst -y OpenSSL.Light
+    - IF %PHP%==1 cinst -y php
+    - cd c:\tools\php
+    - IF %PHP%==1 copy php.ini-production php.ini /Y
+    - IF %PHP%==1 echo date.timezone="UTC" >> php.ini
+    - IF %PHP%==1 echo extension_dir=ext >> php.ini
+    - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
+    - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
+    - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
+    - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
+    - appveyor DownloadFile https://getcomposer.org/composer.phar
+    - cd c:\projects\composer
+    - composer install --prefer-source --no-progress
+
+test_script:
+    - cd c:\projects\composer
+    - vendor/bin/phpunit --colors=always

+ 0 - 9
bin/update-spdx-licenses

@@ -1,9 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-require __DIR__ . '/../src/bootstrap.php';
-
-use Composer\Util\SpdxLicensesUpdater;
-
-$licenses = new SpdxLicensesUpdater;
-$licenses->update();

+ 5 - 2
composer.json

@@ -23,11 +23,14 @@
     },
     "require": {
         "php": ">=5.3.2",
-        "justinrainbow/json-schema": "~1.4",
+        "justinrainbow/json-schema": "^1.4.4",
+        "composer/spdx-licenses": "^1.0",
+        "composer/semver": "^1.0",
         "seld/jsonlint": "~1.0",
         "symfony/console": "~2.5",
         "symfony/finder": "~2.2",
         "symfony/process": "~2.1",
+        "symfony/filesystem": "~2.5",
         "seld/phar-utils": "~1.0",
         "seld/cli-prompt": "~1.0"
     },
@@ -35,7 +38,7 @@
         "phpunit/phpunit": "~4.5",
         "phpunit/phpunit-mock-objects": "2.3.0"
     },
-    "_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223",
+    "_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis",
     "config": {
         "platform": {
             "php": "5.3.3"

+ 279 - 113
composer.lock

@@ -4,24 +4,146 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "556ac817fc0b456bddc48918ef09930d",
+    "hash": "af3956ae4c1a09e3e72cd66346a6176d",
+    "content-hash": "96817117d0ca449e7deff55c98eb7bdc",
     "packages": [
+        {
+            "name": "composer/semver",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/semver.git",
+                "reference": "d0e1ccc6d44ab318b758d709e19176037da6b1ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/semver/zipball/d0e1ccc6d44ab318b758d709e19176037da6b1ba",
+                "reference": "d0e1ccc6d44ab318b758d709e19176037da6b1ba",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.5",
+                "phpunit/phpunit-mock-objects": "~2.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Semver\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com"
+                },
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Semver library that offers utilities, version constraint parsing and validation.",
+            "keywords": [
+                "semantic",
+                "semver",
+                "validation",
+                "versioning"
+            ],
+            "time": "2015-09-21 09:42:36"
+        },
+        {
+            "name": "composer/spdx-licenses",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/spdx-licenses.git",
+                "reference": "324b3530ac3e6277ff4bedf82a34fbf35836eb8d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/324b3530ac3e6277ff4bedf82a34fbf35836eb8d",
+                "reference": "324b3530ac3e6277ff4bedf82a34fbf35836eb8d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.5",
+                "phpunit/phpunit-mock-objects": "~2.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Spdx\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com"
+                },
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "SPDX licenses list and validation library.",
+            "keywords": [
+                "license",
+                "spdx",
+                "validator"
+            ],
+            "time": "2015-09-07 16:25:20"
+        },
         {
             "name": "justinrainbow/json-schema",
-            "version": "1.4.1",
+            "version": "1.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/justinrainbow/json-schema.git",
-                "reference": "2465fe486c864e30badaa4d005ebdf89dbc503f3"
+                "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2465fe486c864e30badaa4d005ebdf89dbc503f3",
-                "reference": "2465fe486c864e30badaa4d005ebdf89dbc503f3",
+                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/a4bee9f4b344b66e0a0d96c7afae1e92edf385fe",
+                "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=5.3.2"
             },
             "require-dev": {
                 "json-schema/json-schema-test-suite": "1.1.0",
@@ -38,8 +160,8 @@
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "JsonSchema": "src/"
+                "psr-4": {
+                    "JsonSchema\\": "src/JsonSchema/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -70,7 +192,7 @@
                 "json",
                 "schema"
             ],
-            "time": "2015-03-27 16:41:39"
+            "time": "2015-09-08 22:28:04"
         },
         {
             "name": "seld/cli-prompt",
@@ -212,17 +334,17 @@
         },
         {
             "name": "symfony/console",
-            "version": "v2.6.9",
+            "version": "v2.6.11",
             "target-dir": "Symfony/Component/Console",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Console.git",
-                "reference": "b5ec0c11a204718f2b656357f5505a8e578f30dd"
+                "reference": "0e5e18ae09d3f5c06367759be940e9ed3f568359"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Console/zipball/b5ec0c11a204718f2b656357f5505a8e578f30dd",
-                "reference": "b5ec0c11a204718f2b656357f5505a8e578f30dd",
+                "url": "https://api.github.com/repos/symfony/Console/zipball/0e5e18ae09d3f5c06367759be940e9ed3f568359",
+                "reference": "0e5e18ae09d3f5c06367759be940e9ed3f568359",
                 "shasum": ""
             },
             "require": {
@@ -266,21 +388,71 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2015-05-29 14:42:58"
+            "time": "2015-07-26 09:08:40"
+        },
+        {
+            "name": "symfony/filesystem",
+            "version": "v2.6.11",
+            "target-dir": "Symfony/Component/Filesystem",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Filesystem.git",
+                "reference": "823c035b1a5c13a4924e324d016eb07e70f94735"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Filesystem/zipball/823c035b1a5c13a4924e324d016eb07e70f94735",
+                "reference": "823c035b1a5c13a4924e324d016eb07e70f94735",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "~2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Filesystem\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Filesystem Component",
+            "homepage": "https://symfony.com",
+            "time": "2015-07-08 05:59:48"
         },
         {
             "name": "symfony/finder",
-            "version": "v2.6.9",
+            "version": "v2.6.11",
             "target-dir": "Symfony/Component/Finder",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Finder.git",
-                "reference": "ffedd3e0ff8155188155e9322fe21b9ee012ac14"
+                "reference": "203a10f928ae30176deeba33512999233181dd28"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Finder/zipball/ffedd3e0ff8155188155e9322fe21b9ee012ac14",
-                "reference": "ffedd3e0ff8155188155e9322fe21b9ee012ac14",
+                "url": "https://api.github.com/repos/symfony/Finder/zipball/203a10f928ae30176deeba33512999233181dd28",
+                "reference": "203a10f928ae30176deeba33512999233181dd28",
                 "shasum": ""
             },
             "require": {
@@ -316,21 +488,21 @@
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2015-05-15 13:32:45"
+            "time": "2015-07-09 16:02:48"
         },
         {
             "name": "symfony/process",
-            "version": "v2.6.9",
+            "version": "v2.6.11",
             "target-dir": "Symfony/Component/Process",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Process.git",
-                "reference": "7856d78ab6cce6e59d02d9e1a873441f6bd21306"
+                "reference": "57f1e88bb5dafa449b83f9f265b11d52d517b3e9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Process/zipball/7856d78ab6cce6e59d02d9e1a873441f6bd21306",
-                "reference": "7856d78ab6cce6e59d02d9e1a873441f6bd21306",
+                "url": "https://api.github.com/repos/symfony/Process/zipball/57f1e88bb5dafa449b83f9f265b11d52d517b3e9",
+                "reference": "57f1e88bb5dafa449b83f9f265b11d52d517b3e9",
                 "shasum": ""
             },
             "require": {
@@ -366,22 +538,22 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2015-05-15 13:32:45"
+            "time": "2015-06-30 16:10:16"
         }
     ],
     "packages-dev": [
         {
             "name": "doctrine/instantiator",
-            "version": "1.0.4",
+            "version": "1.0.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119"
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119",
-                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
                 "shasum": ""
             },
             "require": {
@@ -392,7 +564,7 @@
                 "ext-pdo": "*",
                 "ext-phar": "*",
                 "phpunit/phpunit": "~4.0",
-                "squizlabs/php_codesniffer": "2.0.*@ALPHA"
+                "squizlabs/php_codesniffer": "~2.0"
             },
             "type": "library",
             "extra": {
@@ -401,8 +573,8 @@
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "Doctrine\\Instantiator\\": "src"
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -422,7 +594,7 @@
                 "constructor",
                 "instantiate"
             ],
-            "time": "2014-10-13 12:58:55"
+            "time": "2015-06-14 21:17:01"
         },
         {
             "name": "phpdocumentor/reflection-docblock",
@@ -475,16 +647,16 @@
         },
         {
             "name": "phpspec/prophecy",
-            "version": "v1.4.1",
+            "version": "v1.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373"
+                "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
-                "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
+                "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
                 "shasum": ""
             },
             "require": {
@@ -531,20 +703,20 @@
                 "spy",
                 "stub"
             ],
-            "time": "2015-04-27 22:15:08"
+            "time": "2015-08-13 10:07:40"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "2.1.5",
+            "version": "2.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "be2286cb8c7e1773eded49d9719219e6f74f9e3e"
+                "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be2286cb8c7e1773eded49d9719219e6f74f9e3e",
-                "reference": "be2286cb8c7e1773eded49d9719219e6f74f9e3e",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c",
+                "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c",
                 "shasum": ""
             },
             "require": {
@@ -552,7 +724,7 @@
                 "phpunit/php-file-iterator": "~1.3",
                 "phpunit/php-text-template": "~1.2",
                 "phpunit/php-token-stream": "~1.3",
-                "sebastian/environment": "~1.0",
+                "sebastian/environment": "^1.3.2",
                 "sebastian/version": "~1.0"
             },
             "require-dev": {
@@ -567,7 +739,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.1.x-dev"
+                    "dev-master": "2.2.x-dev"
                 }
             },
             "autoload": {
@@ -593,20 +765,20 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2015-06-09 13:05:42"
+            "time": "2015-08-04 03:42:39"
         },
         {
             "name": "phpunit/php-file-iterator",
-            "version": "1.4.0",
+            "version": "1.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb"
+                "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb",
-                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+                "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
                 "shasum": ""
             },
             "require": {
@@ -640,20 +812,20 @@
                 "filesystem",
                 "iterator"
             ],
-            "time": "2015-04-02 05:19:05"
+            "time": "2015-06-21 13:08:43"
         },
         {
             "name": "phpunit/php-text-template",
-            "version": "1.2.0",
+            "version": "1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-text-template.git",
-                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
-                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
                 "shasum": ""
             },
             "require": {
@@ -662,20 +834,17 @@
             "type": "library",
             "autoload": {
                 "classmap": [
-                    "Text/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
+                    "email": "sebastian@phpunit.de",
                     "role": "lead"
                 }
             ],
@@ -684,20 +853,20 @@
             "keywords": [
                 "template"
             ],
-            "time": "2014-01-30 17:20:04"
+            "time": "2015-06-21 13:50:34"
         },
         {
             "name": "phpunit/php-timer",
-            "version": "1.0.5",
+            "version": "1.0.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+                "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
-                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
+                "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
                 "shasum": ""
             },
             "require": {
@@ -706,13 +875,10 @@
             "type": "library",
             "autoload": {
                 "classmap": [
-                    "PHP/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
@@ -728,20 +894,20 @@
             "keywords": [
                 "timer"
             ],
-            "time": "2013-08-02 07:42:54"
+            "time": "2015-06-21 08:01:12"
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "1.4.1",
+            "version": "1.4.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "eab81d02569310739373308137284e0158424330"
+                "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/eab81d02569310739373308137284e0158424330",
-                "reference": "eab81d02569310739373308137284e0158424330",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3ab72c62e550370a6cd5dc873e1a04ab57562f5b",
+                "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b",
                 "shasum": ""
             },
             "require": {
@@ -777,20 +943,20 @@
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2015-04-08 04:46:07"
+            "time": "2015-08-16 08:51:00"
         },
         {
             "name": "phpunit/phpunit",
-            "version": "4.7.2",
+            "version": "4.8.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "8e0c63329c8c4185296b8d357daa5c6bae43080f"
+                "reference": "2246830f4a1a551c67933e4171bf2126dc29d357"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e0c63329c8c4185296b8d357daa5c6bae43080f",
-                "reference": "8e0c63329c8c4185296b8d357daa5c6bae43080f",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2246830f4a1a551c67933e4171bf2126dc29d357",
+                "reference": "2246830f4a1a551c67933e4171bf2126dc29d357",
                 "shasum": ""
             },
             "require": {
@@ -800,15 +966,15 @@
                 "ext-reflection": "*",
                 "ext-spl": "*",
                 "php": ">=5.3.3",
-                "phpspec/prophecy": "~1.3,>=1.3.1",
+                "phpspec/prophecy": "^1.3.1",
                 "phpunit/php-code-coverage": "~2.1",
                 "phpunit/php-file-iterator": "~1.4",
                 "phpunit/php-text-template": "~1.2",
-                "phpunit/php-timer": "~1.0",
+                "phpunit/php-timer": ">=1.0.6",
                 "phpunit/phpunit-mock-objects": "~2.3",
                 "sebastian/comparator": "~1.1",
                 "sebastian/diff": "~1.2",
-                "sebastian/environment": "~1.2",
+                "sebastian/environment": "~1.3",
                 "sebastian/exporter": "~1.2",
                 "sebastian/global-state": "~1.0",
                 "sebastian/version": "~1.0",
@@ -823,7 +989,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.7.x-dev"
+                    "dev-master": "4.8.x-dev"
                 }
             },
             "autoload": {
@@ -849,7 +1015,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2015-06-06 08:36:08"
+            "time": "2015-08-24 04:09:38"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
@@ -908,16 +1074,16 @@
         },
         {
             "name": "sebastian/comparator",
-            "version": "1.1.1",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "1dd8869519a225f7f2b9eb663e225298fade819e"
+                "reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e",
-                "reference": "1dd8869519a225f7f2b9eb663e225298fade819e",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
+                "reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
                 "shasum": ""
             },
             "require": {
@@ -931,7 +1097,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1.x-dev"
+                    "dev-master": "1.2.x-dev"
                 }
             },
             "autoload": {
@@ -968,7 +1134,7 @@
                 "compare",
                 "equality"
             ],
-            "time": "2015-01-29 16:28:08"
+            "time": "2015-07-26 15:48:44"
         },
         {
             "name": "sebastian/diff",
@@ -1024,16 +1190,16 @@
         },
         {
             "name": "sebastian/environment",
-            "version": "1.2.2",
+            "version": "1.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e"
+                "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e",
-                "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44",
+                "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44",
                 "shasum": ""
             },
             "require": {
@@ -1070,20 +1236,20 @@
                 "environment",
                 "hhvm"
             ],
-            "time": "2015-01-01 10:01:08"
+            "time": "2015-08-03 06:14:51"
         },
         {
             "name": "sebastian/exporter",
-            "version": "1.2.0",
+            "version": "1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "84839970d05254c73cde183a721c7af13aede943"
+                "reference": "7ae5513327cb536431847bcc0c10edba2701064e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943",
-                "reference": "84839970d05254c73cde183a721c7af13aede943",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
+                "reference": "7ae5513327cb536431847bcc0c10edba2701064e",
                 "shasum": ""
             },
             "require": {
@@ -1136,7 +1302,7 @@
                 "export",
                 "exporter"
             ],
-            "time": "2015-01-27 07:23:06"
+            "time": "2015-06-21 07:55:53"
         },
         {
             "name": "sebastian/global-state",
@@ -1191,16 +1357,16 @@
         },
         {
             "name": "sebastian/recursion-context",
-            "version": "1.0.0",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "3989662bbb30a29d20d9faa04a846af79b276252"
+                "reference": "994d4a811bafe801fb06dccbee797863ba2792ba"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252",
-                "reference": "3989662bbb30a29d20d9faa04a846af79b276252",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba",
+                "reference": "994d4a811bafe801fb06dccbee797863ba2792ba",
                 "shasum": ""
             },
             "require": {
@@ -1240,20 +1406,20 @@
             ],
             "description": "Provides functionality to recursively process PHP variables",
             "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
-            "time": "2015-01-24 09:48:32"
+            "time": "2015-06-21 08:04:50"
         },
         {
             "name": "sebastian/version",
-            "version": "1.0.5",
+            "version": "1.0.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/version.git",
-                "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4"
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4",
-                "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
                 "shasum": ""
             },
             "type": "library",
@@ -1275,21 +1441,21 @@
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
             "homepage": "https://github.com/sebastianbergmann/version",
-            "time": "2015-02-24 06:35:25"
+            "time": "2015-06-21 13:59:46"
         },
         {
             "name": "symfony/yaml",
-            "version": "v2.6.9",
+            "version": "v2.6.11",
             "target-dir": "Symfony/Component/Yaml",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Yaml.git",
-                "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2"
+                "reference": "c044d1744b8e91aaaa0d9bac683ab87ec7cbf359"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Yaml/zipball/f157ab074e453ecd4c0fa775f721f6e67a99d9e2",
-                "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2",
+                "url": "https://api.github.com/repos/symfony/Yaml/zipball/c044d1744b8e91aaaa0d9bac683ab87ec7cbf359",
+                "reference": "c044d1744b8e91aaaa0d9bac683ab87ec7cbf359",
                 "shasum": ""
             },
             "require": {
@@ -1325,7 +1491,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2015-05-02 15:18:45"
+            "time": "2015-07-26 08:59:42"
         }
     ],
     "aliases": [],

+ 67 - 86
doc/00-intro.md

@@ -1,54 +1,41 @@
 # Introduction
 
 Composer is a tool for dependency management in PHP. It allows you to declare
-the dependent libraries your project needs and it will install them in your
-project for you.
+the libraries your project depends on and it will manage (install/update) them
+for you.
 
 ## Dependency management
 
-Composer is not a package manager. Yes, it deals with "packages" or libraries, but
-it manages them on a per-project basis, installing them in a directory (e.g. `vendor`)
-inside your project. By default it will never install anything globally. Thus,
-it is a dependency manager.
+Composer is **not** a package manager in the same sense as Yum or Apt are. Yes,
+it deals with "packages" or libraries, but it manages them on a per-project
+basis, installing them in a directory (e.g. `vendor`) inside your project. By
+default it will never install anything globally. Thus, it is a dependency
+manager.
 
-This idea is not new and Composer is strongly inspired by node's [npm](https://npmjs.org/)
-and ruby's [bundler](http://bundler.io/). But there has not been such a tool
-for PHP.
+This idea is not new and Composer is strongly inspired by node's
+[npm](https://npmjs.org/) and ruby's [bundler](http://bundler.io/).
 
-The problem that Composer solves is this:
+Suppose:
 
 a) You have a project that depends on a number of libraries.
 
 b) Some of those libraries depend on other libraries.
 
-c) You declare the things you depend on.
+Composer:
 
-d) Composer finds out which versions of which packages need to be installed, and
-   installs them (meaning it downloads them into your project).
-
-## Declaring dependencies
+c) Enables you to declare the libraries you depend on.
 
-Let's say you are creating a project, and you need a library that does logging.
-You decide to use [monolog](https://github.com/Seldaek/monolog). In order to
-add it to your project, all you need to do is create a `composer.json` file
-which describes the project's dependencies.
-
-```json
-{
-    "require": {
-        "monolog/monolog": "1.2.*"
-    }
-}
-```
+d) Finds out which versions of which packages can and need to be installed, and
+   installs them (meaning it downloads them into your project).
 
-We are simply stating that our project requires some `monolog/monolog` package,
-any version beginning with `1.2`.
+See the [Basic usage](01-basic-usage.md) chapter for more details on declaring
+dependencies.
 
 ## System Requirements
 
 Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile
-flags are also required, but when using the installer you will be warned about any
-incompatibilities.
+flags are also required, but when using the installer you will be warned about
+any incompatibilities.
 
 To install packages from sources instead of simple zip archives, you will need
 git, svn or hg depending on how the package is version-controlled.
@@ -60,6 +47,12 @@ Linux and OSX.
 
 ### Downloading the Composer Executable
 
+Composer offers a convenient installer that you can execute directly from the
+commandline. Feel free to [download this file](https://getcomposer.org/installer)
+or review it on [GitHub](https://github.com/composer/getcomposer.org/blob/master/web/installer)
+if you wish to know more about the inner workings of the installer. The source
+is plain PHP.
+
 There are in short, two ways to install Composer. Locally as part of your
 project, or globally as a system wide executable.
 
@@ -79,37 +72,54 @@ curl -sS https://getcomposer.org/installer | php
 php -r "readfile('https://getcomposer.org/installer');" | php
 ```
 
-The installer will just check a few PHP settings and then download `composer.phar`
-to your working directory. This file is the Composer binary. It is a PHAR (PHP
-archive), which is an archive format for PHP which can be run on the command
-line, amongst other things.
+The installer will just check a few PHP settings and then download
+`composer.phar` to your working directory. This file is the Composer binary. It
+is a PHAR (PHP archive), which is an archive format for PHP which can be run on
+the command line, amongst other things.
+
+Now just run `php composer.phar` in order to run Composer.
 
 You can install Composer to a specific directory by using the `--install-dir`
-option and providing a target directory (it can be an absolute or relative path):
+option and additionally (re)name it as well using the `--filename` option:
 
 ```sh
-curl -sS https://getcomposer.org/installer | php -- --install-dir=bin
+curl -sS https://getcomposer.org/installer | php -- --install-dir=bin --filename=composer
 ```
 
+Now just run `php bin/composer` in order to run Composer.
+
 #### Globally
 
-You can place this file anywhere you wish. If you put it in your `PATH`,
-you can access it globally. On unixy systems you can even make it
-executable and invoke it without `php`.
+You can place the Composer PHAR anywhere you wish. If you put it in a directory
+that is part of your `PATH`, you can access it globally. On unixy systems you
+can even make it executable and invoke it without directly using the `php`
+interpreter.
 
-You can run these commands to easily access `composer` from anywhere on your system:
+Run these commands to globally install `composer` on your system:
 
 ```sh
 curl -sS https://getcomposer.org/installer | php
 mv composer.phar /usr/local/bin/composer
 ```
 
-> **Note:** If the above fails due to permissions, run the `mv` line
-> again with sudo.
+> **Note:** If the above fails due to permissions, run the `mv` line again 
+> with sudo.
+
+A quick copy-paste version including sudo:
 
-> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you receive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/` manually before proceeding.
+```sh
+curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
+```
 
-Then, just run `composer` in order to run Composer instead of `php composer.phar`.
+> **Note:** On some versions of OSX the `/usr` directory does not exist by
+> default. If you receive the error "/usr/local/bin/composer: No such file or
+> directory" then you must create the directory manually before proceeding:
+> `mkdir -p /usr/local/bin`.
+
+> **Note:** For information on changing your PATH, please read the
+> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use Google.
+
+Now just run `composer` in order to run Composer instead of `php composer.phar`.
 
 ## Installation - Windows
 
@@ -117,24 +127,26 @@ Then, just run `composer` in order to run Composer instead of `php composer.phar
 
 This is the easiest way to get Composer set up on your machine.
 
-Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe),
-it will install the latest Composer version and set up your PATH so that you can
-just call `composer` from any directory in your command line.
+Download and run
+[Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). It will
+install the latest Composer version and set up your PATH so that you can just
+call `composer` from any directory in your command line.
 
-> **Note:** Close your current terminal. Test usage with a new terminal:
-> That is important since the PATH only gets loaded when the terminal starts.
+> **Note:** Close your current terminal. Test usage with a new terminal: This is
+> important since the PATH only gets loaded when the terminal starts.
 
 ### Manual Installation
 
 Change to a directory on your `PATH` and run the install snippet to download
-composer.phar:
+`composer.phar`:
 
 ```sh
 C:\Users\username>cd C:\bin
 C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php
 ```
 
-> **Note:** If the above fails due to readfile, use the `http` url or enable php_openssl.dll in php.ini
+> **Note:** If the above fails due to readfile, use the `http` url or enable
+> php_openssl.dll in php.ini
 
 Create a new `composer.bat` file alongside `composer.phar`:
 
@@ -153,38 +165,7 @@ Composer version 27d8904
 
 ## Using Composer
 
-We will now use Composer to install the dependencies of the project. If you
-don't have a `composer.json` file in the current directory please skip to the
-[Basic Usage](01-basic-usage.md) chapter.
-
-To resolve and download dependencies, run the `install` command:
-
-```sh
-php composer.phar install
-```
-
-If you did a global install and do not have the phar in that directory
-run this instead:
-
-```sh
-composer install
-```
-
-Following the [example above](#declaring-dependencies), this will download
-monolog into the `vendor/monolog/monolog` directory.
-
-## Autoloading
-
-Besides downloading the library, Composer also prepares an autoload file that's
-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:
-
-```php
-require __DIR__ . '/vendor/autoload.php';
-```
-
-Woah! Now start using monolog! To keep learning more about Composer, keep
-reading the "Basic Usage" chapter.
+Now that you've installed Composer, you are ready to use it! Head on over to the
+next chapter for a short and simple demonstration.
 
-[Basic Usage](01-basic-usage.md) &rarr;
+[Basic usage](01-basic-usage.md) &rarr;

+ 79 - 115
doc/01-basic-usage.md

@@ -1,8 +1,13 @@
 # Basic usage
 
-## Installing
+## Introduction
 
-If you have not yet installed Composer, refer to the [Intro](00-intro.md) chapter.
+For our basic usage introduction, we will be installing `monolog/monolog`,
+a logging library. If you have not yet installed Composer, refer to the
+[Intro](00-intro.md) chapter.
+
+> **Note:** for the sake of simplicity, this introduction will assume you
+> have performed a [local](00-intro.md#locally) install of Composer.
 
 ## `composer.json`: Project Setup
 
@@ -10,14 +15,11 @@ To start using Composer in your project, all you need is a `composer.json`
 file. This file describes the dependencies of your project and may contain
 other metadata as well.
 
-The [JSON format](http://json.org/) is quite easy to write. It allows you to
-define nested structures.
-
 ### The `require` Key
 
 The first (and often only) thing you specify in `composer.json` is the
-`require` key. You're simply telling Composer which packages your project
-depends on.
+[`require`](04-schema.md#require) key. You're simply telling Composer which
+packages your project depends on.
 
 ```json
 {
@@ -27,15 +29,16 @@ depends on.
 }
 ```
 
-As you can see, `require` takes an object that maps **package names** (e.g. `monolog/monolog`)
-to **package versions** (e.g. `1.0.*`).
+As you can see, [`require`](04-schema.md#require) takes an object that maps
+**package names** (e.g. `monolog/monolog`) to **version constraints** (e.g.
+`1.0.*`).
 
 ### Package Names
 
 The package name consists of a vendor name and the project's name. Often these
-will be identical - the vendor name just exists to prevent naming clashes. It allows
-two different people to create a library named `json`, which would then just be
-named `igorw/json` and `seldaek/json`.
+will be identical - the vendor name just exists to prevent naming clashes. It
+allows two different people to create a library named `json`, which would then
+just be named `igorw/json` and `seldaek/json`.
 
 Here we are requiring `monolog/monolog`, so the vendor name is the same as the
 project's name. For projects with a unique name this is recommended. It also
@@ -45,66 +48,26 @@ smaller decoupled parts.
 
 ### Package Versions
 
-In the previous example we were requiring version [`1.0.*`](http://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*) of monolog. This
-means any version in the `1.0` development branch. It would match `1.0.0`,
-`1.0.2` or `1.0.20`.
-
-Version constraints can be specified in a few different ways.
-
-Name           | Example                                                                  | Description
--------------- | ------------------------------------------------------------------------ | -----------
-Exact version  | `1.0.2`                                                                  | You can specify the exact version of a package.
-Range          | `>=1.0` `>=1.0 <2.0` <code>&gt;=1.0 &lt;1.1 &#124;&#124; &gt;=1.2</code> | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`. <br />You can define multiple ranges. Ranges separated by a space (<code> </code>) or comma (`,`) will be treated as a **logical AND**. A double pipe (<code>&#124;&#124;</code>) will be treated as a **logical OR**. AND has higher precedence than OR.
-Hyphen Range   | `1.0 - 2.0`                                                              | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`.
-Wildcard       | `1.0.*`                                                                  | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`.
-Tilde Operator | `~1.2`                                                                   | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2 <2.0`. For more details, read the next section below.
-Caret Operator | `^1.2.3`                                                                 | Very useful for projects that follow semantic versioning. `^1.2.3` is equivalent to `>=1.2.3 <2.0`. For more details, read the next section below.
-
-### Next Significant Release (Tilde and Caret Operators)
-
-The `~` operator is best explained by example: `~1.2` is equivalent to
-`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
-it is mostly useful for projects respecting [semantic
-versioning](http://semver.org/). A common usage would be to mark the minimum
-minor version you depend on, like `~1.2` (which allows anything up to, but not
-including, 2.0). Since in theory there should be no backwards compatibility
-breaks until 2.0, that works well. Another way of looking at it is that using
-`~` specifies a minimum version, but allows the last digit specified to go up.
-
-The `^` operator behaves very similarly but it sticks closer to semantic
-versioning, and will always allow non-breaking updates. For example `^1.2.3`
-is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should
-break backwards compatibility. For pre-1.0 versions it also acts with safety
-in mind and treats `^0.3` as `>=0.3.0 <0.4.0`
-
-> **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint
-> like `~1.2` would not install it. As said above `~1.2` only means the `.2`
-> can change but the `1.` part is fixed.
-
-> **Note:** The `~` operator has an exception on its behavior for the major
-> release number. This means for example that `~1` is the same as `~1.0` as
-> it will not allow the major number to increase trying to keep backwards
-> compatibility.
+In the previous example we were requiring version
+[`1.0.*`](http://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*) of
+Monolog. This means any version in the `1.0` development branch. It is the
+equivalent of saying versions that match `>=1.0 <1.1`.
+
+Version constraints can be specified in several ways, read
+[versions](articles/versions.md) for more in-depth information on this topic.
 
 ### Stability
 
-By default only stable releases are taken into consideration. If you would like
-to also get RC, beta, alpha or dev versions of your dependencies you can do
-so using [stability flags](04-schema.md#package-links). To change that for all
-packages instead of doing per dependency you can also use the
+By default only stable releases are taken into consideration. If you would
+like to also get RC, beta, alpha or dev versions of your dependencies you can
+do so using [stability flags](04-schema.md#package-links). To change that for
+all packages instead of doing per dependency you can also use the
 [minimum-stability](04-schema.md#minimum-stability) setting.
 
-### Test version constraints
-
-You can test version constraints using [semver.mwl.be](http://semver.mwl.be). Fill in
-a package name and it will autofill the default version constraint which Composer would add
-to your `composer.json` file. You can adjust the version constraint and the tool will highlight
-all releases that match.
-
 ## Installing Dependencies
 
-To fetch the defined dependencies into your local project, just run the
-`install` command of `composer.phar`.
+To install the defined dependencies for your project, just run the
+[`install`](03-cli.md#install) command.
 
 ```sh
 php composer.phar install
@@ -113,14 +76,14 @@ php composer.phar install
 This will find the latest version of `monolog/monolog` that matches the
 supplied version constraint and download it into the `vendor` directory.
 It's a convention to put third party code into a directory named `vendor`.
-In case of monolog it will put it into `vendor/monolog/monolog`.
+In case of Monolog it will put it into `vendor/monolog/monolog`.
 
 > **Tip:** If you are using git for your project, you probably want to add
-> `vendor` into your `.gitignore`. You really don't want to add all of that
+> `vendor` in your `.gitignore`. You really don't want to add all of that
 > code to your repository.
 
-Another thing that the `install` command does is it adds a `composer.lock`
-file into your project root.
+You will notice the [`install`](03-cli.md#install) command also created a
+`composer.lock` file.
 
 ## `composer.lock` - The Lock File
 
@@ -128,82 +91,82 @@ After installing the dependencies, Composer writes the list of the exact
 versions it installed into a `composer.lock` file. This locks the project
 to those specific versions.
 
-**Commit your application's `composer.lock` (along with `composer.json`) into version control.**
+**Commit your application's `composer.lock` (along with `composer.json`)
+into version control.**
 
-This is important because the `install` command checks if a lock file is present,
-and if it is, it downloads the versions specified there (regardless of what `composer.json`
-says).
+This is important because the [`install`](03-cli.md#install) command checks
+if a lock file is present, and if it is, it downloads the versions specified
+there (regardless of what `composer.json` says).
 
-This means that anyone who sets up the project will download the exact
-same version of the dependencies. Your CI server, production machines, other
-developers in your team, everything and everyone runs on the same dependencies, which
-mitigates the potential for bugs affecting only some parts of the deployments. Even if you
-develop alone, in six months when reinstalling the project you can feel confident the
-dependencies installed are still working even if your dependencies released
-many new versions since then.
+This means that anyone who sets up the project will download the exact same
+version of the dependencies. Your CI server, production machines, other
+developers in your team, everything and everyone runs on the same dependencies,
+which mitigates the potential for bugs affecting only some parts of the
+deployments. Even if you develop alone, in six months when reinstalling the
+project you can feel confident the dependencies installed are still working even
+if your dependencies released many new versions since then.
 
 If no `composer.lock` file exists, Composer will read the dependencies and
-versions from `composer.json` and  create the lock file after executing the `update` or the `install`
-command.
+versions from `composer.json` and  create the lock file after executing the
+[`update`](03-cli.md#update) or the [`install`](03-cli.md#install) command.
 
-This means that if any of the dependencies get a new version, you won't get the updates
-automatically. To update to the new version, use the `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.
+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 the
+[`update`](03-cli.md#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.
 
 ```sh
 php composer.phar update
 ```
-> **Note:** Composer will display a Warning when executing an `install` command if 
- `composer.lock` and `composer.json` are not synchronized.
- 
+> **Note:** Composer will display a Warning when executing an `install` command
+> if `composer.lock` and `composer.json` are not synchronized.
+
 If you only want to install or update one dependency, you can whitelist them:
 
 ```sh
 php composer.phar update monolog/monolog [...]
 ```
 
-> **Note:** For libraries it is not necessarily recommended to commit the lock file,
-> see also: [Libraries - Lock file](02-libraries.md#lock-file).
+> **Note:** For libraries it is not necessary to commit the lock
+> file, see also: [Libraries - Lock file](02-libraries.md#lock-file).
 
 ## Packagist
 
 [Packagist](https://packagist.org/) is the main Composer repository. A Composer
 repository is basically a package source: a place where you can get packages
 from. Packagist aims to be the central repository that everybody uses. This
-means that you can automatically `require` any package that is available
-there.
+means that you can automatically `require` any package that is available there.
 
-If you go to the [packagist website](https://packagist.org/) (packagist.org),
+If you go to the [Packagist website](https://packagist.org/) (packagist.org),
 you can browse and search for packages.
 
-Any open source project using Composer should publish their packages on
-packagist. A library doesn't need to be on packagist to be used by Composer,
-but it makes life quite a bit simpler.
+Any open source project using Composer is recommended to publish their packages
+on Packagist. A library doesn't need to be on Packagist to be used by Composer,
+but it enables discovery and adoption by other developers more quickly.
 
 ## Autoloading
 
 For libraries that specify autoload information, Composer generates a
-`vendor/autoload.php` file. You can simply include this file and you
-will get autoloading for free.
+`vendor/autoload.php` file. You can simply include this file and you will get
+autoloading for free.
 
 ```php
-require 'vendor/autoload.php';
+require __DIR__ . '/vendor/autoload.php';
 ```
 
-This makes it really easy to use third party code. For example: If your
-project depends on monolog, you can just start using classes from it, and they
-will be autoloaded.
+This makes it really easy to use third party code. For example: If your project
+depends on Monolog, you can just start using classes from it, and they will be
+autoloaded.
 
 ```php
 $log = new Monolog\Logger('name');
 $log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
-
 $log->addWarning('Foo');
 ```
 
-You can even add your own code to the autoloader by adding an `autoload` field
-to `composer.json`.
+You can even add your own code to the autoloader by adding an
+[`autoload`](04-schema.md#autoload) field to `composer.json`.
 
 ```json
 {
@@ -220,24 +183,25 @@ You define a mapping from namespaces to directories. The `src` directory would
 be in your project root, on the same level as `vendor` directory is. An example
 filename would be `src/Foo.php` containing an `Acme\Foo` class.
 
-After adding the `autoload` field, you have to re-run `dump-autoload` to re-generate
-the `vendor/autoload.php` file.
+After adding the [`autoload`](04-schema.md#autoload) field, you have to re-run
+[`dump-autoload`](03-cli.md#dump-autoload) to re-generate 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.
 
 ```php
-$loader = require 'vendor/autoload.php';
+$loader = require __DIR__ . '/vendor/autoload.php';
 $loader->add('Acme\\Test\\', __DIR__);
 ```
 
-In addition to PSR-4 autoloading, classmap is also supported. This allows
-classes to be autoloaded even if they do not conform to PSR-4. See the
-[autoload reference](04-schema.md#autoload) for more details.
+In addition to PSR-4 autoloading, Composer also supports PSR-0, classmap and
+files autoloading. See the [`autoload`](04-schema.md#autoload) reference for
+more information.
 
-> **Note:** Composer provides its own autoloader. If you don't want to use
-that one, you can just include `vendor/composer/autoload_*.php` files,
-which return associative arrays allowing you to configure your own autoloader.
+> **Note:** Composer provides its own autoloader. If you don't want to use that
+> one, you can just include `vendor/composer/autoload_*.php` files, which return
+> associative arrays allowing you to configure your own autoloader.
 
 &larr; [Intro](00-intro.md)  |  [Libraries](02-libraries.md) &rarr;

+ 39 - 37
doc/02-libraries.md

@@ -1,16 +1,17 @@
 # Libraries
 
-This chapter will tell you how to make your library installable through Composer.
+This chapter will tell you how to make your library installable through
+Composer.
 
 ## Every project is a package
 
 As soon as you have a `composer.json` in a directory, that directory is a
-package. When you add a `require` to a project, you are making a package that
-depends on other packages. The only difference between your project and
-libraries is that your project is a package without a name.
+package. When you add a [`require`](04-schema.md#require) to a project, you are
+making a package that depends on other packages. The only difference between
+your project and libraries is that your project is a package without a name.
 
 In order to make that package installable you need to give it a name. You do
-this by adding a `name` to `composer.json`:
+this by adding the [`name`](04-schema.md#name) property in `composer.json`:
 
 ```json
 {
@@ -21,12 +22,12 @@ this by adding a `name` to `composer.json`:
 }
 ```
 
-In this case the project name is `acme/hello-world`, where `acme` is the
-vendor name. Supplying a vendor name is mandatory.
+In this case the project name is `acme/hello-world`, where `acme` is the vendor
+name. Supplying a vendor name is mandatory.
 
 > **Note:** If you don't know what to use as a vendor name, your GitHub
-username is usually a good bet. While package names are case insensitive, the
-convention is all lowercase and dashes for word separation.
+> username is usually a good bet. While package names are case insensitive, the
+> convention is all lowercase and dashes for word separation.
 
 ## Platform packages
 
@@ -50,15 +51,14 @@ includes PHP itself, PHP extensions and some system libraries.
   PHP. The following are available: `curl`, `iconv`, `icu`, `libxml`,
   `openssl`, `pcre`, `uuid`, `xsl`.
 
-You can use `composer show --platform` to get a list of your locally available
-platform packages.
+You can use [`show --platform`](03-cli.md#show) to get a list of your locally
+available platform packages.
 
 ## Specifying the version
 
-You need to specify the package's version some way. When you publish your
-package on Packagist, it is able to infer the version from the VCS (git, svn,
-hg) information, so in that case you do not have to specify it, and it is
-recommended not to. See [tags](#tags) and [branches](#branches) to see how
+When you publish your package on Packagist, it is able to infer the version
+from the VCS (git, svn, hg) information. This means you don't have to
+explicitly declare it. Read [tags](#tags) and [branches](#branches) to see how
 version numbers are extracted from these.
 
 If you are creating packages by hand and really have to specify it explicitly,
@@ -76,9 +76,9 @@ you can just add a `version` field:
 ### Tags
 
 For every tag that looks like a version, a package version of that tag will be
-created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix
-of `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffixes
-can also be followed by a number.
+created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix of
+`-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffix can also
+be followed by a number.
 
 Here are a few examples of valid tag names:
 
@@ -89,19 +89,20 @@ Here are a few examples of valid tag names:
 - v2.0.0-alpha
 - v2.0.4-p1
 
-> **Note:** Even if your tag is prefixed with `v`, a [version constraint](01-basic-usage.md#package-versions)
-> in a `require` statement has to be specified without prefix
-> (e.g. tag `v1.0.0` will result in version `1.0.0`). 
+> **Note:** Even if your tag is prefixed with `v`, a
+> [version constraint](01-basic-usage.md#package-versions) in a `require`
+> statement has to be specified without prefix (e.g. tag `v1.0.0` will result
+> in version `1.0.0`).
 
 ### Branches
 
 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,
-the branch `2.0` will get the `2.0.x-dev` version (the `.x` is added for technical
-reasons, to make sure it is recognized as a branch). The `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.
+the branch `2.0` will get the `2.0.x-dev` version (the `.x` is added for
+technical reasons, to make sure it is recognized as a branch). The `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:
 
@@ -116,8 +117,8 @@ Here are some examples of version branch names:
 ### Aliases
 
 It is possible to alias branch names to versions. For example, you could alias
-`dev-master` to `1.0.x-dev`, which would allow you to require `1.0.x-dev` in all
-the packages.
+`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.
 
@@ -133,7 +134,7 @@ the `.gitignore`.
 
 ## Publishing to a VCS
 
-Once you have a vcs repository (version control system, e.g. git) containing a
+Once you have a VCS repository (version control system, e.g. git) containing a
 `composer.json` file, your library is already composer-installable. In this
 example we will publish the `acme/hello-world` library on GitHub under
 `github.com/username/hello-world`.
@@ -180,11 +181,11 @@ For more details on how package repositories work and what other types are
 available, see [Repositories](05-repositories.md).
 
 That's all. You can now install the dependencies by running Composer's
-`install` command!
+[`install`](03-cli.md#install) command!
 
 **Recap:** Any git/svn/hg repository containing a `composer.json` can be added
 to your project by specifying the package repository and declaring the
-dependency in the `require` field.
+dependency in the [`require`](04-schema.md#require) field.
 
 ## Publishing to packagist
 
@@ -196,15 +197,16 @@ repository for `monolog/monolog`. How did that work? The answer is Packagist.
 
 [Packagist](https://packagist.org/) is the main package repository for
 Composer, and it is enabled by default. Anything that is published on
-Packagist is available automatically through Composer. Since monolog
-[is on packagist](https://packagist.org/packages/monolog/monolog), we can depend
-on it without having to specify any additional repositories.
+Packagist is available automatically through Composer. Since
+[Monolog is on Packagist](https://packagist.org/packages/monolog/monolog), we
+can depend on it without having to specify any additional repositories.
 
 If we wanted to share `hello-world` with the world, we would publish it on
 Packagist as well. Doing so is really easy.
 
-You simply hit the big "Submit Package" button and sign up. Then you submit
-the URL to your VCS repository, at which point Packagist will start crawling
-it. Once it is done, your package will be available to anyone.
+You simply visit [Packagist](https://packagist.org) and hit the "Submit". This
+will prompt you to sign up if you haven't already, and then allows you to
+submit the URL to your VCS repository, at which point Packagist will start
+crawling it. Once it is done, your package will be available to anyone!
 
 &larr; [Basic usage](01-basic-usage.md) |  [Command-line interface](03-cli.md) &rarr;

+ 41 - 8
doc/03-cli.md

@@ -87,7 +87,7 @@ resolution.
   installing a package, you can use `--dry-run`. This will simulate the
   installation and show you what would happen.
 * **--dev:** Install packages listed in `require-dev` (this is the default behavior).
-* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader 
+* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader
   generation skips the `autoload-dev` rules.
 * **--no-autoloader:** Skips autoloader generation.
 * **--no-scripts:** Skips execution of scripts defined in `composer.json`.
@@ -97,6 +97,8 @@ resolution.
 * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
   autoloader. This is recommended especially for production, but can take
   a bit of time to run so it is currently not done by default.
+* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
+  Implicitly enables `--optimize-autoloader`.
 
 ## update
 
@@ -140,9 +142,11 @@ php composer.phar update vendor/*
 * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
   autoloader. This is recommended especially for production, but can take
   a bit of time to run so it is currently not done by default.
+* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
+  Implicitly enables `--optimize-autoloader`.
 * **--lock:** Only updates the lock file hash to suppress warning about the
   lock file being out of date.
-* **--with-dependencies** Add also all dependencies of whitelisted packages to the whitelist.
+* **--with-dependencies:** Add also all dependencies of whitelisted packages to the whitelist.
 * **--prefer-stable:** Prefer stable versions of dependencies.
 * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
   versions of requirements, generally used with `--prefer-stable`.
@@ -177,9 +181,15 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master
 * **--no-update:** Disables the automatic update of the dependencies.
 * **--no-progress:** Removes the progress display that can mess with some
   terminals or scripts which don't handle backspace characters.
-* **--update-no-dev** Run the dependency update with the --no-dev option.
-* **--update-with-dependencies** Also update dependencies of the newly
+* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
+* **--update-with-dependencies:** Also update dependencies of the newly
   required packages.
+* **--sort-packages:** Keep packages sorted in `composer.json`.
+* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to
+  get a faster autoloader. This is recommended especially for production, but
+  can take a bit of time to run so it is currently not done by default.
+* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
+  Implicitly enables `--optimize-autoloader`.
 
 ## remove
 
@@ -201,8 +211,13 @@ uninstalled.
 * **--no-update:** Disables the automatic update of the dependencies.
 * **--no-progress:** Removes the progress display that can mess with some
   terminals or scripts which don't handle backspace characters.
-* **--update-no-dev** Run the dependency update with the --no-dev option.
-* **--update-with-dependencies** Also update dependencies of the removed packages.
+* **--update-no-dev:** Run the dependency update with the --no-dev option.
+* **--update-with-dependencies:** Also update dependencies of the removed packages.
+* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to
+  get a faster autoloader. This is recommended especially for production, but
+  can take a bit of time to run so it is currently not done by default.
+* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
+  Implicitly enables `--optimize-autoloader`.
 
 ## global
 
@@ -294,6 +309,18 @@ in your browser.
 
 * **--homepage (-H):** Open the homepage instead of the repository URL.
 
+## suggests
+
+Lists all packages suggested by currently installed set of packages. You can
+optionally pass one or multiple package names in the format of `vendor/package`
+to limit output to suggestions made by those packages only.
+
+### Options
+
+* **--no-dev:** Excludes suggestions from `require-dev` packages.
+* **--verbose (-v):** Increased verbosity adds suggesting package name and
+  reason for suggestion.
+
 ## depends
 
 The `depends` command tells you which other packages depend on a certain
@@ -327,7 +354,9 @@ php composer.phar validate
 
 ### Options
 
-* **--no-check-all:** Whether or not Composer does a complete validation.
+* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound version constraints.
+* **--no-check-lock:** Do not emit an error if `composer.lock` exists and is not up to date.
+* **--no-check-publish:** Do not emit an error if `composer.json` is unsuitable for publishing as a package on Packagist but is otherwise valid.
 
 ## status
 
@@ -375,7 +404,7 @@ sudo composer self-update
 ### Options
 
 * **--rollback (-r):** Rollback to the last version you had installed.
-* **--clean-backups:** Delete old backups during an update. This makes the 
+* **--clean-backups:** Delete old backups during an update. This makes the
   current version of Composer the only backup available after the update.
 
 ## config
@@ -490,6 +519,8 @@ performance.
 * **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
   autoloader. This is recommended especially for production, but can take
   a bit of time to run so it is currently not done by default.
+* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
+  Implicitly enables `--optimize`.
 * **--no-dev:** Disables autoload-dev rules.
 
 ## clear-cache
@@ -568,6 +599,8 @@ For example:
 COMPOSER=composer-other.json php composer.phar install
 ```
 
+The generated lock file will use the same name: `composer-other.lock` in this example.
+
 ### COMPOSER_ROOT_VERSION
 
 By setting this var you can specify the version of the root package, if it can

+ 40 - 0
doc/05-repositories.md

@@ -263,6 +263,10 @@ custom repository has priority over packagist. If you want to rename the
 package, you should do so in the default (often master) branch and not in a
 feature branch, since the package name is taken from the default branch.
 
+Also note that the override will not work if you change the `name` property
+in your forked repository's composer.json file as this needs to match the
+original for the override to work.
+
 If other dependencies rely on the package you forked, it is possible to
 inline-alias it so that it matches a constraint that it otherwise would not.
 For more information [see the aliases article](articles/aliases.md).
@@ -602,6 +606,42 @@ imported. When an archive with a newer version is added in the artifact folder
 and you run `update`, that version will be imported as well and Composer will
 update to the latest version.
 
+### Path
+
+In addition to the artifact repository, you can use the path one, which allows
+you to depend on a relative directory. This can be especially useful when dealing
+with monolith repositories.
+
+For instance, if you have the following directory structure in your repository:
+```
+- apps
+\_ my-app
+  \_ composer.json
+- packages
+\_ my-package
+  \_ composer.json
+```
+
+Then, to add the package `my/package` as a dependency, in your `apps/my-app/composer.json`
+file, you can use the following configuration:
+
+```json
+{
+    "repositories": [
+        {
+            "type": "path",
+            "url": "../../packages/my-package"
+        }
+    ],
+    "require": {
+        "my/package": "*@dev"
+    }
+}
+```
+
+> **Note:** Repository paths can also contain wildcards like ``*`` and ``?``.
+> For details, see the [PHP glob function](http://php.net/glob).
+
 ## Disabling Packagist
 
 You can disable the default Packagist repository by adding this to your

+ 10 - 11
doc/06-config.md

@@ -46,6 +46,11 @@ A list of domain names and username/passwords to authenticate against them. For
 example using `{"example.org": {"username": "alice", "password": "foo"}` as the
 value of this option will let Composer authenticate against example.org.
 
+> **Note:** Authentication-related config options like `http-basic` and
+> `github-oauth` can also be specified inside a `auth.json` file that goes
+> besides your `composer.json`. That way you can gitignore it and every
+> developer can place their own credentials in there.
+
 ## platform
 
 Lets you fake platform packages (PHP and extensions) so that you can emulate a
@@ -100,7 +105,7 @@ first until the cache fits.
 
 ## prepend-autoloader
 
-Defaults to `true`. If false, the Composer autoloader will not be prepended to
+Defaults to `true`. If `false`, the Composer autoloader will not be prepended to
 existing autoloaders. This is sometimes required to fix interoperability issues
 with other autoloaders.
 
@@ -111,13 +116,12 @@ autoloader. When null a random one will be generated.
 
 ## optimize-autoloader
 
-Defaults to `false`. Always optimize when dumping the autoloader.
+Defaults to `false`. If `true`, always optimize when dumping the autoloader.
 
 ## classmap-authoritative
 
-Defaults to `false`. If `true`, the Composer autoloader will not scan the
-filesystem for classes that are not found in the class map. Implies
-'optimize-autoloader'.
+Defaults to `false`. If `true`, the Composer autoloader will only load classes
+from the classmap. Implies `optimize-autoloader`.
 
 ## github-domains
 
@@ -126,7 +130,7 @@ used for GitHub Enterprise setups.
 
 ## github-expose-hostname
 
-Defaults to `true`. If set to `false`, the OAuth tokens created to access the
+Defaults to `true`. If `false`, the OAuth tokens created to access the
 github API will have a date instead of the machine hostname.
 
 ## notify-on-install
@@ -164,9 +168,4 @@ Example:
 }
 ```
 
-> **Note:** Authentication-related config options like `http-basic` and
-> `github-oauth` can also be specified inside a `auth.json` file that goes
-> besides your `composer.json`. That way you can gitignore it and every
-> developer can place their own credentials in there.
-
 &larr; [Repositories](05-repositories.md)  |  [Community](07-community.md) &rarr;

+ 1 - 1
doc/articles/handling-private-packages-with-satis.md

@@ -73,7 +73,7 @@ constraint if you want really specific versions.
 ```
 
 Once you've done this, you just run `php bin/satis build <configuration file> <build dir>`.
-For example `php bin/satis build config.json web/` would read the `config.json`
+For example `php bin/satis build satis.json web/` would read the `satis.json`
 file and build a static repository inside the `web/` directory.
 
 When you ironed out that process, what you would typically do is run this

+ 27 - 12
doc/articles/scripts.md

@@ -23,34 +23,34 @@ Composer fires the following named events during its execution process:
 ### Command Events
 
 - **pre-install-cmd**: occurs before the `install` command is executed.
-- **post-install-cmd**: occurs after the `install` command is executed.
+- **post-install-cmd**: occurs after the `install` command has been executed.
 - **pre-update-cmd**: occurs before the `update` command is executed.
-- **post-update-cmd**: occurs after the `update` command is executed.
+- **post-update-cmd**: occurs after the `update` command has been executed.
 - **pre-status-cmd**: occurs before the `status` command is executed.
-- **post-status-cmd**: occurs after the `status` command is executed.
+- **post-status-cmd**: occurs after the `status` command has been executed.
 - **pre-archive-cmd**: occurs before the `archive` command is executed.
-- **post-archive-cmd**: occurs after the `archive` command is executed.
+- **post-archive-cmd**: occurs after the `archive` command has been executed.
 - **pre-autoload-dump**: occurs before the autoloader is dumped, either
   during `install`/`update`, or via the `dump-autoload` command.
-- **post-autoload-dump**: occurs after the autoloader is dumped, either
+- **post-autoload-dump**: occurs after the autoloader has been dumped, either
   during `install`/`update`, or via the `dump-autoload` command.
 - **post-root-package-install**: occurs after the root package has been
   installed, during the `create-project` command.
-- **post-create-project-cmd**: occurs after the `create-project` command is
-  executed.
+- **post-create-project-cmd**: occurs after the `create-project` command has
+  been executed.
 
 ### Installer Events
 
 - **pre-dependencies-solving**: occurs before the dependencies are resolved.
-- **post-dependencies-solving**: occurs after the dependencies are resolved.
+- **post-dependencies-solving**: occurs after the dependencies have been resolved.
 
 ### Package Events
 
 - **pre-package-install**: occurs before a package is installed.
-- **post-package-install**: occurs after a package is installed.
+- **post-package-install**: occurs after a package has been installed.
 - **pre-package-update**: occurs before a package is updated.
-- **post-package-update**: occurs after a package is updated.
-- **pre-package-uninstall**: occurs before a package has been uninstalled.
+- **post-package-update**: occurs after a package has been updated.
+- **pre-package-uninstall**: occurs before a package is uninstalled.
 - **post-package-uninstall**: occurs after a package has been uninstalled.
 
 ### Plugin Events
@@ -82,6 +82,10 @@ For any given event:
 and command-line executable commands.
 - PHP classes containing defined callbacks must be autoloadable via Composer's
 autoload functionality.
+- Callbacks can only autoload classes from psr-0, psr-4 and classmap
+definitions. If a defined callback relies on functions defined outside of a
+class, the callback itself is responsible for loading the file containing these
+functions.
 
 Script definition example:
 
@@ -96,7 +100,10 @@ Script definition example:
             "MyVendor\\MyClass::warmCache",
             "phpunit -c app/"
         ],
-        "post-create-project-cmd" : [
+        "post-autoload-dump": [
+            "MyVendor\\MyClass::postAutoloadDump"
+        ],
+        "post-create-project-cmd": [
             "php -r \"copy('config/local-example.php', 'config/local.php');\""
         ]
     }
@@ -122,6 +129,14 @@ class MyClass
         // do stuff
     }
 
+    public static function postAutoloadDump(Event $event)
+    {
+        $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
+        require $vendorDir . '/autoload.php';
+
+        some_function_from_an_autoloaded_file();
+    }
+
     public static function postPackageInstall(PackageEvent $event)
     {
         $installedPackage = $event->getOperation()->getPackage();

+ 36 - 7
doc/articles/troubleshooting.md

@@ -43,6 +43,10 @@ This is a list of common pitfalls on using Composer, and how to avoid them.
 5. If you are updating to a recently published version of a package, be aware that
    Packagist has a delay of up to 1 minute before new packages are visible to Composer.
 
+6. If you are updating a single package, it may depend on newer versions itself.
+   In this case add the `--with-dependencies` argument **or** add all dependencies which
+   need an update to the command.
+
 ## Package not found on travis-ci.org
 
 1. Check the ["Package not found"](#package-not-found) item above.
@@ -61,13 +65,13 @@ This is a list of common pitfalls on using Composer, and how to avoid them.
 ## Package not found in a Jenkins-build
 
 1. Check the ["Package not found"](#package-not-found) item above.
-2. Reason for failing is similar to the problem which can occur on travis-ci.org: The 
-   git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As 
-   a result, Composer is not able to identify the version of the current checked out branch 
-   and may not be able to resolve a cyclic dependency. To solve this problem, you can use 
-   the "Additional Behaviours" -> "Check out to specific local branch" in your Git-settings 
-   for your Jenkins-job, where your "local branch" shall be the same branch as you are 
-   checking out. Using this, the checkout will not be in detached state any more and cyclic 
+2. Reason for failing is similar to the problem which can occur on travis-ci.org: The
+   git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As
+   a result, Composer is not able to identify the version of the current checked out branch
+   and may not be able to resolve a cyclic dependency. To solve this problem, you can use
+   the "Additional Behaviours" -> "Check out to specific local branch" in your Git-settings
+   for your Jenkins-job, where your "local branch" shall be the same branch as you are
+   checking out. Using this, the checkout will not be in detached state any more and cyclic
    dependency is recognized correctly.
 
 ## Need to override a package version
@@ -168,3 +172,28 @@ To enable the swap you can use for example:
 /sbin/mkswap /var/swap.1
 /sbin/swapon /var/swap.1
 ```
+
+## Degraded Mode
+
+Due to some intermittent issues on Travis and other systems, we introduced a
+degraded network mode which helps Composer finish successfully but disables
+a few optimizations. This is enabled automatically when an issue is first
+detected. If you see this issue sporadically you probably don't have to worry
+(a slow or overloaded network can also cause those time outs), but if it
+appears repeatedly you might want to look at the options below to identify
+and resolve it.
+
+If you have been pointed to this page, you want to check a few things:
+
+- If you are using ESET antivirus, go in "Advanced Settings" and disable "HTTP-scanner"
+  under "web access protection"
+- If you are using IPv6, try disabling it. If that solves your issues, get in touch
+  with your ISP or server host, the problem is not at the Packagist level but in the
+  routing rules between you and Packagist (i.e. the internet at large). The best way to get
+  these fixed is raise awareness to the network engineers that have the power to fix it.
+
+  To disable IPv6 on Linux, try using this command which appends a
+  rule preferring IPv4 over IPv6 to your config:
+
+  `sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"`
+- If none of the above helped, please report the error.

+ 118 - 0
doc/articles/versions.md

@@ -0,0 +1,118 @@
+<!--
+    tagline: Version constraints explained.
+-->
+
+# Versions
+
+## Basic Constraints
+
+### Exact
+
+You can specify the exact version of a package. This will tell Composer to
+install this version and this version only. If other dependencies require
+a different version, the solver will ultimately fail and abort any install
+or update procedures.
+
+Example: `1.0.2`
+
+### Range
+
+By using comparison operators you can specify ranges of valid versions. Valid
+operators are `>`, `>=`, `<`, `<=`, `!=`.
+
+You can define multiple ranges. Ranges separated by a space (<code>&nbsp;</code>)
+or comma (`,`) will be treated as a **logical AND**. A double pipe (`||`)
+will be treated as a **logical OR**. AND has higher precedence than OR.
+
+> **Note:** Be careful when using unbounded ranges as you might end up
+> unexpectedly installing versions that break backwards compatibility.
+> Consider using the [caret](#caret) operator instead for safety.
+
+Examples:
+
+* `>=1.0`
+* `>=1.0 <2.0`
+* `>=1.0 <1.1 || >=1.2`
+
+### Range (Hyphen)
+
+Inclusive set of versions. Partial versions on the right include are completed
+with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the
+`2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to
+`>=1.0.0 <=2.1.0`.
+
+Example: `1.0 - 2.0`
+
+### Wildcard
+
+You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of
+`>=1.0 <1.1`.
+
+Example: `1.0.*`
+
+## Next Significant Release Operators
+
+### Tilde
+
+The `~` operator is best explained by example: `~1.2` is equivalent to
+`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
+it is mostly useful for projects respecting [semantic
+versioning](http://semver.org/). A common usage would be to mark the minimum
+minor version you depend on, like `~1.2` (which allows anything up to, but not
+including, 2.0). Since in theory there should be no backwards compatibility
+breaks until 2.0, that works well. Another way of looking at it is that using
+`~` specifies a minimum version, but allows the last digit specified to go up.
+
+Example: `~1.2`
+
+> **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint
+> like `~1.2` would not install it. As said above `~1.2` only means the `.2`
+> can change but the `1.` part is fixed.
+
+> **Note:** The `~` operator has an exception on its behavior for the major
+> release number. This means for example that `~1` is the same as `~1.0` as
+> it will not allow the major number to increase trying to keep backwards
+> compatibility.
+
+### Caret
+
+The `^` operator behaves very similarly but it sticks closer to semantic
+versioning, and will always allow non-breaking updates. For example `^1.2.3`
+is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should
+break backwards compatibility. For pre-1.0 versions it also acts with safety
+in mind and treats `^0.3` as `>=0.3.0 <0.4.0`.
+
+This is the recommended operator for maximum interoperability when writing
+library code.
+
+Example: `^1.2.3`
+
+## Stability
+
+If you are using a constraint that does not explicitly define a stability,
+Composer will default internally to `-dev` or `-stable`, depending on the
+operator(s) used. This happens transparently.
+
+If you wish to explicitly consider only the stable release in the comparison,
+add the suffix `-stable`.
+
+Examples:
+
+ Constraint         | Internally
+------------------- | ------------------------
+ `1.2.3`            | `=1.2.3.0-stable`
+ `>1.2`             | `>1.2.0.0-stable`
+ `>=1.2`            | `>=1.2.0.0-dev`
+ `>=1.2-stable`     | `>=1.2.0.0-stable`
+ `<1.3`             | `<1.3.0.0-dev`
+ `<=1.3`            | `<=1.3.0.0-stable`
+ `1 - 2`            | `>=1.0.0.0-dev <3.0.0.0-dev`
+ `~1.3`             | `>=1.3.0.0-dev <2.0.0.0-dev`
+ `1.4.*`            | `>=1.4.0.0-dev <1.5.0.0-dev`
+
+## Test version constraints
+
+You can test version constraints using [semver.mwl.be](http://semver.mwl.be).
+Fill in a package name and it will autofill the default version constraint
+which Composer would add to your `composer.json` file. You can adjust the
+version constraint and the tool will highlight all releases that match.

+ 0 - 1226
res/spdx-licenses.json

@@ -1,1226 +0,0 @@
-{
-    "Glide": [
-        "3dfx Glide License",
-        false
-    ],
-    "Abstyles": [
-        "Abstyles License",
-        false
-    ],
-    "AFL-1.1": [
-        "Academic Free License v1.1",
-        true
-    ],
-    "AFL-1.2": [
-        "Academic Free License v1.2",
-        true
-    ],
-    "AFL-2.0": [
-        "Academic Free License v2.0",
-        true
-    ],
-    "AFL-2.1": [
-        "Academic Free License v2.1",
-        true
-    ],
-    "AFL-3.0": [
-        "Academic Free License v3.0",
-        true
-    ],
-    "AMPAS": [
-        "Academy of Motion Picture Arts and Sciences BSD",
-        false
-    ],
-    "APL-1.0": [
-        "Adaptive Public License 1.0",
-        true
-    ],
-    "Adobe-Glyph": [
-        "Adobe Glyph List License",
-        false
-    ],
-    "APAFML": [
-        "Adobe Postscript AFM License",
-        false
-    ],
-    "Adobe-2006": [
-        "Adobe Systems Incorporated Source Code License Agreement",
-        false
-    ],
-    "AGPL-1.0": [
-        "Affero General Public License v1.0",
-        false
-    ],
-    "Afmparse": [
-        "Afmparse License",
-        false
-    ],
-    "Aladdin": [
-        "Aladdin Free Public License",
-        false
-    ],
-    "ADSL": [
-        "Amazon Digital Services License",
-        false
-    ],
-    "AMDPLPA": [
-        "AMD's plpa_map.c License",
-        false
-    ],
-    "ANTLR-PD": [
-        "ANTLR Software Rights Notice",
-        false
-    ],
-    "Apache-1.0": [
-        "Apache License 1.0",
-        false
-    ],
-    "Apache-1.1": [
-        "Apache License 1.1",
-        true
-    ],
-    "Apache-2.0": [
-        "Apache License 2.0",
-        true
-    ],
-    "AML": [
-        "Apple MIT License",
-        false
-    ],
-    "APSL-1.0": [
-        "Apple Public Source License 1.0",
-        true
-    ],
-    "APSL-1.1": [
-        "Apple Public Source License 1.1",
-        true
-    ],
-    "APSL-1.2": [
-        "Apple Public Source License 1.2",
-        true
-    ],
-    "APSL-2.0": [
-        "Apple Public Source License 2.0",
-        true
-    ],
-    "Artistic-1.0": [
-        "Artistic License 1.0",
-        true
-    ],
-    "Artistic-1.0-Perl": [
-        "Artistic License 1.0 (Perl)",
-        true
-    ],
-    "Artistic-1.0-cl8": [
-        "Artistic License 1.0 w/clause 8",
-        true
-    ],
-    "Artistic-2.0": [
-        "Artistic License 2.0",
-        true
-    ],
-    "AAL": [
-        "Attribution Assurance License",
-        true
-    ],
-    "Bahyph": [
-        "Bahyph License",
-        false
-    ],
-    "Barr": [
-        "Barr License",
-        false
-    ],
-    "Beerware": [
-        "Beerware License",
-        false
-    ],
-    "BitTorrent-1.0": [
-        "BitTorrent Open Source License v1.0",
-        false
-    ],
-    "BitTorrent-1.1": [
-        "BitTorrent Open Source License v1.1",
-        false
-    ],
-    "BSL-1.0": [
-        "Boost Software License 1.0",
-        true
-    ],
-    "Borceux": [
-        "Borceux license",
-        false
-    ],
-    "BSD-2-Clause": [
-        "BSD 2-clause \"Simplified\" License",
-        true
-    ],
-    "BSD-2-Clause-FreeBSD": [
-        "BSD 2-clause FreeBSD License",
-        false
-    ],
-    "BSD-2-Clause-NetBSD": [
-        "BSD 2-clause NetBSD License",
-        false
-    ],
-    "BSD-3-Clause": [
-        "BSD 3-clause \"New\" or \"Revised\" License",
-        true
-    ],
-    "BSD-3-Clause-Clear": [
-        "BSD 3-clause Clear License",
-        false
-    ],
-    "BSD-4-Clause": [
-        "BSD 4-clause \"Original\" or \"Old\" License",
-        false
-    ],
-    "BSD-Protection": [
-        "BSD Protection License",
-        false
-    ],
-    "BSD-3-Clause-Attribution": [
-        "BSD with attribution",
-        false
-    ],
-    "BSD-4-Clause-UC": [
-        "BSD-4-Clause (University of California-Specific)",
-        false
-    ],
-    "bzip2-1.0.5": [
-        "bzip2 and libbzip2 License v1.0.5",
-        false
-    ],
-    "bzip2-1.0.6": [
-        "bzip2 and libbzip2 License v1.0.6",
-        false
-    ],
-    "Caldera": [
-        "Caldera License",
-        false
-    ],
-    "CECILL-1.0": [
-        "CeCILL Free Software License Agreement v1.0",
-        false
-    ],
-    "CECILL-1.1": [
-        "CeCILL Free Software License Agreement v1.1",
-        false
-    ],
-    "CECILL-2.0": [
-        "CeCILL Free Software License Agreement v2.0",
-        false
-    ],
-    "CECILL-B": [
-        "CeCILL-B Free Software License Agreement",
-        false
-    ],
-    "CECILL-C": [
-        "CeCILL-C Free Software License Agreement",
-        false
-    ],
-    "ClArtistic": [
-        "Clarified Artistic License",
-        false
-    ],
-    "MIT-CMU": [
-        "CMU License",
-        false
-    ],
-    "CNRI-Python": [
-        "CNRI Python License",
-        true
-    ],
-    "CNRI-Python-GPL-Compatible": [
-        "CNRI Python Open Source GPL Compatible License Agreement",
-        false
-    ],
-    "CPOL-1.02": [
-        "Code Project Open License 1.02",
-        false
-    ],
-    "CDDL-1.0": [
-        "Common Development and Distribution License 1.0",
-        true
-    ],
-    "CDDL-1.1": [
-        "Common Development and Distribution License 1.1",
-        false
-    ],
-    "CPAL-1.0": [
-        "Common Public Attribution License 1.0",
-        true
-    ],
-    "CPL-1.0": [
-        "Common Public License 1.0",
-        true
-    ],
-    "CATOSL-1.1": [
-        "Computer Associates Trusted Open Source License 1.1",
-        true
-    ],
-    "Condor-1.1": [
-        "Condor Public License v1.1",
-        false
-    ],
-    "CC-BY-1.0": [
-        "Creative Commons Attribution 1.0",
-        false
-    ],
-    "CC-BY-2.0": [
-        "Creative Commons Attribution 2.0",
-        false
-    ],
-    "CC-BY-2.5": [
-        "Creative Commons Attribution 2.5",
-        false
-    ],
-    "CC-BY-3.0": [
-        "Creative Commons Attribution 3.0",
-        false
-    ],
-    "CC-BY-4.0": [
-        "Creative Commons Attribution 4.0",
-        false
-    ],
-    "CC-BY-ND-1.0": [
-        "Creative Commons Attribution No Derivatives 1.0",
-        false
-    ],
-    "CC-BY-ND-2.0": [
-        "Creative Commons Attribution No Derivatives 2.0",
-        false
-    ],
-    "CC-BY-ND-2.5": [
-        "Creative Commons Attribution No Derivatives 2.5",
-        false
-    ],
-    "CC-BY-ND-3.0": [
-        "Creative Commons Attribution No Derivatives 3.0",
-        false
-    ],
-    "CC-BY-ND-4.0": [
-        "Creative Commons Attribution No Derivatives 4.0",
-        false
-    ],
-    "CC-BY-NC-1.0": [
-        "Creative Commons Attribution Non Commercial 1.0",
-        false
-    ],
-    "CC-BY-NC-2.0": [
-        "Creative Commons Attribution Non Commercial 2.0",
-        false
-    ],
-    "CC-BY-NC-2.5": [
-        "Creative Commons Attribution Non Commercial 2.5",
-        false
-    ],
-    "CC-BY-NC-3.0": [
-        "Creative Commons Attribution Non Commercial 3.0",
-        false
-    ],
-    "CC-BY-NC-4.0": [
-        "Creative Commons Attribution Non Commercial 4.0",
-        false
-    ],
-    "CC-BY-NC-ND-1.0": [
-        "Creative Commons Attribution Non Commercial No Derivatives 1.0",
-        false
-    ],
-    "CC-BY-NC-ND-2.0": [
-        "Creative Commons Attribution Non Commercial No Derivatives 2.0",
-        false
-    ],
-    "CC-BY-NC-ND-2.5": [
-        "Creative Commons Attribution Non Commercial No Derivatives 2.5",
-        false
-    ],
-    "CC-BY-NC-ND-3.0": [
-        "Creative Commons Attribution Non Commercial No Derivatives 3.0",
-        false
-    ],
-    "CC-BY-NC-ND-4.0": [
-        "Creative Commons Attribution Non Commercial No Derivatives 4.0",
-        false
-    ],
-    "CC-BY-NC-SA-1.0": [
-        "Creative Commons Attribution Non Commercial Share Alike 1.0",
-        false
-    ],
-    "CC-BY-NC-SA-2.0": [
-        "Creative Commons Attribution Non Commercial Share Alike 2.0",
-        false
-    ],
-    "CC-BY-NC-SA-2.5": [
-        "Creative Commons Attribution Non Commercial Share Alike 2.5",
-        false
-    ],
-    "CC-BY-NC-SA-3.0": [
-        "Creative Commons Attribution Non Commercial Share Alike 3.0",
-        false
-    ],
-    "CC-BY-NC-SA-4.0": [
-        "Creative Commons Attribution Non Commercial Share Alike 4.0",
-        false
-    ],
-    "CC-BY-SA-1.0": [
-        "Creative Commons Attribution Share Alike 1.0",
-        false
-    ],
-    "CC-BY-SA-2.0": [
-        "Creative Commons Attribution Share Alike 2.0",
-        false
-    ],
-    "CC-BY-SA-2.5": [
-        "Creative Commons Attribution Share Alike 2.5",
-        false
-    ],
-    "CC-BY-SA-3.0": [
-        "Creative Commons Attribution Share Alike 3.0",
-        false
-    ],
-    "CC-BY-SA-4.0": [
-        "Creative Commons Attribution Share Alike 4.0",
-        false
-    ],
-    "CC0-1.0": [
-        "Creative Commons Zero v1.0 Universal",
-        false
-    ],
-    "Crossword": [
-        "Crossword License",
-        false
-    ],
-    "CUA-OPL-1.0": [
-        "CUA Office Public License v1.0",
-        true
-    ],
-    "Cube": [
-        "Cube License",
-        false
-    ],
-    "D-FSL-1.0": [
-        "Deutsche Freie Software Lizenz",
-        false
-    ],
-    "diffmark": [
-        "diffmark license",
-        false
-    ],
-    "WTFPL": [
-        "Do What The F*ck You Want To Public License",
-        false
-    ],
-    "DOC": [
-        "DOC License",
-        false
-    ],
-    "Dotseqn": [
-        "Dotseqn License",
-        false
-    ],
-    "DSDP": [
-        "DSDP License",
-        false
-    ],
-    "dvipdfm": [
-        "dvipdfm License",
-        false
-    ],
-    "EPL-1.0": [
-        "Eclipse Public License 1.0",
-        true
-    ],
-    "eCos-2.0": [
-        "eCos license version 2.0",
-        false
-    ],
-    "ECL-1.0": [
-        "Educational Community License v1.0",
-        true
-    ],
-    "ECL-2.0": [
-        "Educational Community License v2.0",
-        true
-    ],
-    "eGenix": [
-        "eGenix.com Public License 1.1.0",
-        false
-    ],
-    "EFL-1.0": [
-        "Eiffel Forum License v1.0",
-        true
-    ],
-    "EFL-2.0": [
-        "Eiffel Forum License v2.0",
-        true
-    ],
-    "MIT-advertising": [
-        "Enlightenment License (e16)",
-        false
-    ],
-    "MIT-enna": [
-        "enna License",
-        false
-    ],
-    "Entessa": [
-        "Entessa Public License v1.0",
-        true
-    ],
-    "ErlPL-1.1": [
-        "Erlang Public License v1.1",
-        false
-    ],
-    "EUDatagrid": [
-        "EU DataGrid Software License",
-        true
-    ],
-    "EUPL-1.0": [
-        "European Union Public License 1.0",
-        false
-    ],
-    "EUPL-1.1": [
-        "European Union Public License 1.1",
-        true
-    ],
-    "Eurosym": [
-        "Eurosym License",
-        false
-    ],
-    "Fair": [
-        "Fair License",
-        true
-    ],
-    "MIT-feh": [
-        "feh License",
-        false
-    ],
-    "Frameworx-1.0": [
-        "Frameworx Open License 1.0",
-        true
-    ],
-    "FTL": [
-        "Freetype Project License",
-        false
-    ],
-    "FSFUL": [
-        "FSF Unlimited License",
-        false
-    ],
-    "FSFULLR": [
-        "FSF Unlimited License (with License Retention)",
-        false
-    ],
-    "Giftware": [
-        "Giftware License",
-        false
-    ],
-    "GL2PS": [
-        "GL2PS License",
-        false
-    ],
-    "Glulxe": [
-        "Glulxe License",
-        false
-    ],
-    "AGPL-3.0": [
-        "GNU Affero General Public License v3.0",
-        true
-    ],
-    "GFDL-1.1": [
-        "GNU Free Documentation License v1.1",
-        false
-    ],
-    "GFDL-1.2": [
-        "GNU Free Documentation License v1.2",
-        false
-    ],
-    "GFDL-1.3": [
-        "GNU Free Documentation License v1.3",
-        false
-    ],
-    "GPL-1.0": [
-        "GNU General Public License v1.0 only",
-        false
-    ],
-    "GPL-1.0+": [
-        "GNU General Public License v1.0 or later",
-        false
-    ],
-    "GPL-2.0": [
-        "GNU General Public License v2.0 only",
-        true
-    ],
-    "GPL-2.0+": [
-        "GNU General Public License v2.0 or later",
-        true
-    ],
-    "GPL-2.0-with-autoconf-exception": [
-        "GNU General Public License v2.0 w/Autoconf exception",
-        true
-    ],
-    "GPL-2.0-with-bison-exception": [
-        "GNU General Public License v2.0 w/Bison exception",
-        true
-    ],
-    "GPL-2.0-with-classpath-exception": [
-        "GNU General Public License v2.0 w/Classpath exception",
-        true
-    ],
-    "GPL-2.0-with-font-exception": [
-        "GNU General Public License v2.0 w/Font exception",
-        true
-    ],
-    "GPL-2.0-with-GCC-exception": [
-        "GNU General Public License v2.0 w/GCC Runtime Library exception",
-        true
-    ],
-    "GPL-3.0": [
-        "GNU General Public License v3.0 only",
-        true
-    ],
-    "GPL-3.0+": [
-        "GNU General Public License v3.0 or later",
-        true
-    ],
-    "GPL-3.0-with-autoconf-exception": [
-        "GNU General Public License v3.0 w/Autoconf exception",
-        true
-    ],
-    "GPL-3.0-with-GCC-exception": [
-        "GNU General Public License v3.0 w/GCC Runtime Library exception",
-        true
-    ],
-    "LGPL-2.1": [
-        "GNU Lesser General Public License v2.1 only",
-        true
-    ],
-    "LGPL-2.1+": [
-        "GNU Lesser General Public License v2.1 or later",
-        true
-    ],
-    "LGPL-3.0": [
-        "GNU Lesser General Public License v3.0 only",
-        true
-    ],
-    "LGPL-3.0+": [
-        "GNU Lesser General Public License v3.0 or later",
-        true
-    ],
-    "LGPL-2.0": [
-        "GNU Library General Public License v2 only",
-        true
-    ],
-    "LGPL-2.0+": [
-        "GNU Library General Public License v2 or later",
-        true
-    ],
-    "gnuplot": [
-        "gnuplot License",
-        false
-    ],
-    "gSOAP-1.3b": [
-        "gSOAP Public License v1.3b",
-        false
-    ],
-    "HaskellReport": [
-        "Haskell Language Report License",
-        false
-    ],
-    "HPND": [
-        "Historic Permission Notice and Disclaimer",
-        true
-    ],
-    "IBM-pibs": [
-        "IBM PowerPC Initialization and Boot Software",
-        false
-    ],
-    "IPL-1.0": [
-        "IBM Public License v1.0",
-        true
-    ],
-    "ImageMagick": [
-        "ImageMagick License",
-        false
-    ],
-    "iMatix": [
-        "iMatix Standard Function Library Agreement",
-        false
-    ],
-    "Imlib2": [
-        "Imlib2 License",
-        false
-    ],
-    "IJG": [
-        "Independent JPEG Group License",
-        false
-    ],
-    "Intel-ACPI": [
-        "Intel ACPI Software License Agreement",
-        false
-    ],
-    "Intel": [
-        "Intel Open Source License",
-        true
-    ],
-    "IPA": [
-        "IPA Font License",
-        true
-    ],
-    "ISC": [
-        "ISC License",
-        true
-    ],
-    "JasPer-2.0": [
-        "JasPer License",
-        false
-    ],
-    "JSON": [
-        "JSON License",
-        false
-    ],
-    "LPPL-1.3a": [
-        "LaTeX Project Public License 1.3a",
-        false
-    ],
-    "LPPL-1.0": [
-        "LaTeX Project Public License v1.0",
-        false
-    ],
-    "LPPL-1.1": [
-        "LaTeX Project Public License v1.1",
-        false
-    ],
-    "LPPL-1.2": [
-        "LaTeX Project Public License v1.2",
-        false
-    ],
-    "LPPL-1.3c": [
-        "LaTeX Project Public License v1.3c",
-        true
-    ],
-    "Latex2e": [
-        "Latex2e License",
-        false
-    ],
-    "BSD-3-Clause-LBNL": [
-        "Lawrence Berkeley National Labs BSD variant license",
-        false
-    ],
-    "Leptonica": [
-        "Leptonica License",
-        false
-    ],
-    "Libpng": [
-        "libpng License",
-        false
-    ],
-    "libtiff": [
-        "libtiff License",
-        false
-    ],
-    "LPL-1.02": [
-        "Lucent Public License v1.02",
-        true
-    ],
-    "LPL-1.0": [
-        "Lucent Public License Version 1.0",
-        true
-    ],
-    "MakeIndex": [
-        "MakeIndex License",
-        false
-    ],
-    "MTLL": [
-        "Matrix Template Library License",
-        false
-    ],
-    "MS-PL": [
-        "Microsoft Public License",
-        true
-    ],
-    "MS-RL": [
-        "Microsoft Reciprocal License",
-        true
-    ],
-    "MirOS": [
-        "MirOS Licence",
-        true
-    ],
-    "MITNFA": [
-        "MIT +no-false-attribs license",
-        false
-    ],
-    "MIT": [
-        "MIT License",
-        true
-    ],
-    "Motosoto": [
-        "Motosoto License",
-        true
-    ],
-    "MPL-1.0": [
-        "Mozilla Public License 1.0",
-        true
-    ],
-    "MPL-1.1": [
-        "Mozilla Public License 1.1",
-        true
-    ],
-    "MPL-2.0": [
-        "Mozilla Public License 2.0",
-        true
-    ],
-    "MPL-2.0-no-copyleft-exception": [
-        "Mozilla Public License 2.0 (no copyleft exception)",
-        true
-    ],
-    "mpich2": [
-        "mpich2 License",
-        false
-    ],
-    "Multics": [
-        "Multics License",
-        true
-    ],
-    "Mup": [
-        "Mup License",
-        false
-    ],
-    "NASA-1.3": [
-        "NASA Open Source Agreement 1.3",
-        true
-    ],
-    "Naumen": [
-        "Naumen Public License",
-        true
-    ],
-    "NBPL-1.0": [
-        "Net Boolean Public License v1",
-        false
-    ],
-    "NetCDF": [
-        "NetCDF license",
-        false
-    ],
-    "NGPL": [
-        "Nethack General Public License",
-        true
-    ],
-    "NOSL": [
-        "Netizen Open Source License",
-        false
-    ],
-    "NPL-1.0": [
-        "Netscape Public License v1.0",
-        false
-    ],
-    "NPL-1.1": [
-        "Netscape Public License v1.1",
-        false
-    ],
-    "Newsletr": [
-        "Newsletr License",
-        false
-    ],
-    "NLPL": [
-        "No Limit Public License",
-        false
-    ],
-    "Nokia": [
-        "Nokia Open Source License",
-        true
-    ],
-    "NPOSL-3.0": [
-        "Non-Profit Open Software License 3.0",
-        true
-    ],
-    "Noweb": [
-        "Noweb License",
-        false
-    ],
-    "NRL": [
-        "NRL License",
-        false
-    ],
-    "NTP": [
-        "NTP License",
-        true
-    ],
-    "Nunit": [
-        "Nunit License",
-        false
-    ],
-    "OCLC-2.0": [
-        "OCLC Research Public License 2.0",
-        true
-    ],
-    "ODbL-1.0": [
-        "ODC Open Database License v1.0",
-        false
-    ],
-    "PDDL-1.0": [
-        "ODC Public Domain Dedication & License 1.0",
-        false
-    ],
-    "OGTSL": [
-        "Open Group Test Suite License",
-        true
-    ],
-    "OLDAP-2.2.2": [
-        "Open LDAP Public License  2.2.2",
-        false
-    ],
-    "OLDAP-1.1": [
-        "Open LDAP Public License v1.1",
-        false
-    ],
-    "OLDAP-1.2": [
-        "Open LDAP Public License v1.2",
-        false
-    ],
-    "OLDAP-1.3": [
-        "Open LDAP Public License v1.3",
-        false
-    ],
-    "OLDAP-1.4": [
-        "Open LDAP Public License v1.4",
-        false
-    ],
-    "OLDAP-2.0": [
-        "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)",
-        false
-    ],
-    "OLDAP-2.0.1": [
-        "Open LDAP Public License v2.0.1",
-        false
-    ],
-    "OLDAP-2.1": [
-        "Open LDAP Public License v2.1",
-        false
-    ],
-    "OLDAP-2.2": [
-        "Open LDAP Public License v2.2",
-        false
-    ],
-    "OLDAP-2.2.1": [
-        "Open LDAP Public License v2.2.1",
-        false
-    ],
-    "OLDAP-2.3": [
-        "Open LDAP Public License v2.3",
-        false
-    ],
-    "OLDAP-2.4": [
-        "Open LDAP Public License v2.4",
-        false
-    ],
-    "OLDAP-2.5": [
-        "Open LDAP Public License v2.5",
-        false
-    ],
-    "OLDAP-2.6": [
-        "Open LDAP Public License v2.6",
-        false
-    ],
-    "OLDAP-2.7": [
-        "Open LDAP Public License v2.7",
-        false
-    ],
-    "OML": [
-        "Open Market License",
-        false
-    ],
-    "OPL-1.0": [
-        "Open Public License v1.0",
-        false
-    ],
-    "OSL-1.0": [
-        "Open Software License 1.0",
-        true
-    ],
-    "OSL-1.1": [
-        "Open Software License 1.1",
-        false
-    ],
-    "OSL-2.0": [
-        "Open Software License 2.0",
-        true
-    ],
-    "OSL-2.1": [
-        "Open Software License 2.1",
-        true
-    ],
-    "OSL-3.0": [
-        "Open Software License 3.0",
-        true
-    ],
-    "OLDAP-2.8": [
-        "OpenLDAP Public License v2.8",
-        false
-    ],
-    "OpenSSL": [
-        "OpenSSL License",
-        false
-    ],
-    "PHP-3.0": [
-        "PHP License v3.0",
-        true
-    ],
-    "PHP-3.01": [
-        "PHP License v3.01",
-        false
-    ],
-    "Plexus": [
-        "Plexus Classworlds License",
-        false
-    ],
-    "PostgreSQL": [
-        "PostgreSQL License",
-        true
-    ],
-    "psfrag": [
-        "psfrag License",
-        false
-    ],
-    "psutils": [
-        "psutils License",
-        false
-    ],
-    "Python-2.0": [
-        "Python License 2.0",
-        true
-    ],
-    "QPL-1.0": [
-        "Q Public License 1.0",
-        true
-    ],
-    "Qhull": [
-        "Qhull License",
-        false
-    ],
-    "Rdisc": [
-        "Rdisc License",
-        false
-    ],
-    "RPSL-1.0": [
-        "RealNetworks Public Source License v1.0",
-        true
-    ],
-    "RPL-1.1": [
-        "Reciprocal Public License 1.1",
-        true
-    ],
-    "RPL-1.5": [
-        "Reciprocal Public License 1.5",
-        true
-    ],
-    "RHeCos-1.1": [
-        "Red Hat eCos Public License v1.1",
-        false
-    ],
-    "RSCPL": [
-        "Ricoh Source Code Public License",
-        true
-    ],
-    "Ruby": [
-        "Ruby License",
-        false
-    ],
-    "SAX-PD": [
-        "Sax Public Domain Notice",
-        false
-    ],
-    "Saxpath": [
-        "Saxpath License",
-        false
-    ],
-    "SCEA": [
-        "SCEA Shared Source License",
-        false
-    ],
-    "SWL": [
-        "Scheme Widget Library (SWL) Software License Agreement",
-        false
-    ],
-    "SGI-B-1.0": [
-        "SGI Free Software License B v1.0",
-        false
-    ],
-    "SGI-B-1.1": [
-        "SGI Free Software License B v1.1",
-        false
-    ],
-    "SGI-B-2.0": [
-        "SGI Free Software License B v2.0",
-        false
-    ],
-    "OFL-1.0": [
-        "SIL Open Font License 1.0",
-        false
-    ],
-    "OFL-1.1": [
-        "SIL Open Font License 1.1",
-        true
-    ],
-    "SimPL-2.0": [
-        "Simple Public License 2.0",
-        true
-    ],
-    "Sleepycat": [
-        "Sleepycat License",
-        true
-    ],
-    "SNIA": [
-        "SNIA Public License 1.1",
-        false
-    ],
-    "SMLNJ": [
-        "Standard ML of New Jersey License",
-        false
-    ],
-    "StandardML-NJ": [
-        "Standard ML of New Jersey License",
-        false
-    ],
-    "SugarCRM-1.1.3": [
-        "SugarCRM Public License v1.1.3",
-        false
-    ],
-    "SISSL": [
-        "Sun Industry Standards Source License v1.1",
-        true
-    ],
-    "SISSL-1.2": [
-        "Sun Industry Standards Source License v1.2",
-        false
-    ],
-    "SPL-1.0": [
-        "Sun Public License v1.0",
-        true
-    ],
-    "Watcom-1.0": [
-        "Sybase Open Watcom Public License 1.0",
-        true
-    ],
-    "TCL": [
-        "TCL/TK License",
-        false
-    ],
-    "Unlicense": [
-        "The Unlicense",
-        false
-    ],
-    "TMate": [
-        "TMate Open Source License",
-        false
-    ],
-    "TORQUE-1.1": [
-        "TORQUE v2.5+ Software License v1.1",
-        false
-    ],
-    "TOSL": [
-        "Trusster Open Source License",
-        false
-    ],
-    "Unicode-TOU": [
-        "Unicode Terms of Use",
-        false
-    ],
-    "NCSA": [
-        "University of Illinois/NCSA Open Source License",
-        true
-    ],
-    "Vim": [
-        "Vim License",
-        false
-    ],
-    "VOSTROM": [
-        "VOSTROM Public License for Open Source",
-        false
-    ],
-    "VSL-1.0": [
-        "Vovida Software License v1.0",
-        true
-    ],
-    "W3C": [
-        "W3C Software Notice and License",
-        true
-    ],
-    "Wsuipa": [
-        "Wsuipa License",
-        false
-    ],
-    "WXwindows": [
-        "wxWindows Library License",
-        true
-    ],
-    "Xnet": [
-        "X.Net License",
-        true
-    ],
-    "X11": [
-        "X11 License",
-        false
-    ],
-    "Xerox": [
-        "Xerox License",
-        false
-    ],
-    "XFree86-1.1": [
-        "XFree86 License 1.1",
-        false
-    ],
-    "xinetd": [
-        "xinetd License",
-        false
-    ],
-    "xpp": [
-        "XPP License",
-        false
-    ],
-    "XSkat": [
-        "XSkat License",
-        false
-    ],
-    "YPL-1.0": [
-        "Yahoo! Public License v1.0",
-        false
-    ],
-    "YPL-1.1": [
-        "Yahoo! Public License v1.1",
-        false
-    ],
-    "Zed": [
-        "Zed License",
-        false
-    ],
-    "Zend-2.0": [
-        "Zend License v2.0",
-        false
-    ],
-    "Zimbra-1.3": [
-        "Zimbra Public License v1.3",
-        false
-    ],
-    "Zlib": [
-        "zlib License",
-        true
-    ],
-    "zlib-acknowledgement": [
-        "zlib/libpng License with Acknowledgement",
-        false
-    ],
-    "ZPL-1.1": [
-        "Zope Public License 1.1",
-        false
-    ],
-    "ZPL-2.0": [
-        "Zope Public License 2.0",
-        true
-    ],
-    "ZPL-2.1": [
-        "Zope Public License 2.1",
-        false
-    ]
-}

+ 84 - 23
src/Composer/Autoload/AutoloadGenerator.php

@@ -38,8 +38,16 @@ class AutoloadGenerator
      */
     private $io;
 
+    /**
+     * @var bool
+     */
     private $devMode = false;
 
+    /**
+     * @var bool
+     */
+    private $classMapAuthoritative = false;
+
     public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
     {
         $this->eventDispatcher = $eventDispatcher;
@@ -51,8 +59,23 @@ class AutoloadGenerator
         $this->devMode = (boolean) $devMode;
     }
 
+    /**
+     * Whether or not generated autoloader considers the class map
+     * authoritative.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = (boolean) $classMapAuthoritative;
+    }
+
     public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
     {
+        if ($this->classMapAuthoritative) {
+            // Force scanPsr0Packages when classmap is authoritative
+            $scanPsr0Packages = true;
+        }
         $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array(
             'optimize' => (bool) $scanPsr0Packages,
         ));
@@ -63,7 +86,6 @@ class AutoloadGenerator
         $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
         $useGlobalIncludePath = (bool) $config->get('use-include-path');
         $prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
-        $classMapAuthoritative = $config->get('classmap-authoritative');
         $targetDir = $vendorPath.'/'.$targetDir;
         $filesystem->ensureDirectoryExists($targetDir);
 
@@ -174,10 +196,21 @@ EOF;
         // flatten array
         $classMap = array();
         if ($scanPsr0Packages) {
+            $namespacesToScan = array();
+
             // Scan the PSR-0/4 directories for class files, and add them to the class map
             foreach (array('psr-0', 'psr-4') as $psrType) {
                 foreach ($autoloads[$psrType] as $namespace => $paths) {
-                    foreach ($paths as $dir) {
+                    $namespacesToScan[$namespace][] = array('paths' => $paths, 'type' => $psrType);
+                }
+            }
+
+            krsort($namespacesToScan);
+
+            foreach ($namespacesToScan as $namespace => $groups) {
+                foreach ($groups as $group) {
+                    $psrType = $group['type'];
+                    foreach ($group['paths'] as $dir) {
                         $dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir);
                         if (!is_dir($dir)) {
                             continue;
@@ -190,9 +223,14 @@ EOF;
 
                         $namespaceFilter = $namespace === '' ? null : $namespace;
                         foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io, $namespaceFilter) as $class => $path) {
+                            $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
                             if (!isset($classMap[$class])) {
-                                $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
-                                $classMap[$class] = $path.",\n";
+                                $classMap[$class] = $pathCode;
+                            } elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) {
+                                $this->io->writeError(
+                                    '<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
+                                    ' was found in both "'.str_replace(array('$vendorDir . \'', "',\n"), array($vendorPath, ''), $classMap[$class]).'" and "'.$path.'", the first will be used.</warning>'
+                                );
                             }
                         }
                     }
@@ -202,8 +240,15 @@ EOF;
 
         foreach ($autoloads['classmap'] as $dir) {
             foreach (ClassMapGenerator::createMap($dir, null, $this->io) as $class => $path) {
-                $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
-                $classMap[$class] = $path.",\n";
+                $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
+                if (!isset($classMap[$class])) {
+                    $classMap[$class] = $pathCode;
+                } elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) {
+                    $this->io->writeError(
+                        '<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
+                        ' was found in both "'.str_replace(array('$vendorDir . \'', "',\n"), array($vendorPath, ''), $classMap[$class]).'" and "'.$path.'", the first will be used.</warning>'
+                    );
+                }
             }
         }
 
@@ -229,23 +274,23 @@ EOF;
         file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
         file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
         file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
-        if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
-            file_put_contents($targetDir.'/include_paths.php', $includePathFile);
-        }
-        if ($includeFilesFile = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
-            file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
+        $includePathFilePath = $targetDir.'/include_paths.php';
+        if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
+            file_put_contents($includePathFilePath, $includePathFileContents);
+        } elseif (file_exists($includePathFilePath)) {
+            unlink($includePathFilePath);
+        }
+        $includeFilesFilePath = $targetDir.'/autoload_files.php';
+        if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
+            file_put_contents($includeFilesFilePath, $includeFilesFileContents);
+        } elseif (file_exists($includeFilesFilePath)) {
+            unlink($includeFilesFilePath);
         }
         file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
-        file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative));
+        file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader));
 
-        // use stream_copy_to_stream instead of copy
-        // to work around https://bugs.php.net/bug.php?id=64634
-        $sourceLoader = fopen(__DIR__.'/ClassLoader.php', 'r');
-        $targetLoader = fopen($targetDir.'/ClassLoader.php', 'w+');
-        stream_copy_to_stream($sourceLoader, $targetLoader);
-        fclose($sourceLoader);
-        fclose($targetLoader);
-        unset($sourceLoader, $targetLoader);
+        $this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
+        $this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
 
         $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
             'optimize' => (bool) $scanPsr0Packages,
@@ -310,7 +355,7 @@ EOF;
 
         $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
         $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
-        $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage);
+        $classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $mainPackage);
         $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
 
         krsort($psr0);
@@ -453,7 +498,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
 AUTOLOAD;
     }
 
-    protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative)
+    protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
     {
         // TODO the class ComposerAutoloaderInit should be revert to a closure
         // when APC has been fixed:
@@ -530,7 +575,7 @@ PSR4;
 CLASSMAP;
         }
 
-        if ($classMapAuthoritative) {
+        if ($this->classMapAuthoritative) {
             $file .= <<<'CLASSMAPAUTHORITATIVE'
         $loader->setClassMapAuthoritative(true);
 
@@ -727,4 +772,20 @@ FOOTER;
 
         return $sortedPackageMap;
     }
+
+    /**
+     * Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463
+     *
+     * @param string $source
+     * @param string $target
+     */
+    protected function safeCopy($source, $target)
+    {
+        $source = fopen($source, 'r');
+        $target = fopen($target, 'w+');
+
+        stream_copy_to_stream($source, $target);
+        fclose($source);
+        fclose($target);
+    }
 }

+ 4 - 4
src/Composer/Autoload/ClassLoader.php

@@ -351,7 +351,7 @@ class ClassLoader
             foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
                 if (0 === strpos($class, $prefix)) {
                     foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
-                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                             return $file;
                         }
                     }
@@ -361,7 +361,7 @@ class ClassLoader
 
         // PSR-4 fallback dirs
         foreach ($this->fallbackDirsPsr4 as $dir) {
-            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                 return $file;
             }
         }
@@ -380,7 +380,7 @@ class ClassLoader
             foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                 if (0 === strpos($class, $prefix)) {
                     foreach ($dirs as $dir) {
-                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                             return $file;
                         }
                     }
@@ -390,7 +390,7 @@ class ClassLoader
 
         // PSR-0 fallback dirs
         foreach ($this->fallbackDirsPsr0 as $dir) {
-            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                 return $file;
             }
         }

+ 2 - 3
src/Composer/Autoload/ClassMapGenerator.php

@@ -49,9 +49,8 @@ class ClassMapGenerator
      * @param IOInterface      $io        IO object
      * @param string           $namespace Optional namespace prefix to filter by
      *
-     * @return array A class map array
-     *
      * @throws \RuntimeException When the path is neither an existing file nor directory
+     * @return array             A class map array
      */
     public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null)
     {
@@ -91,7 +90,7 @@ class ClassMapGenerator
 
                 if (!isset($map[$class])) {
                     $map[$class] = $filePath;
-                } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
+                } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
                     $io->writeError(
                         '<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
                         ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'

+ 9 - 4
src/Composer/Cache.php

@@ -43,10 +43,12 @@ class Cache
         $this->whitelist = $whitelist;
         $this->filesystem = $filesystem ?: new Filesystem();
 
-        if (!is_dir($this->root)) {
-            if (!@mkdir($this->root, 0777, true)) {
-                $this->enabled = false;
-            }
+        if (
+            (!is_dir($this->root) && !@mkdir($this->root, 0777, true))
+            || !is_writable($this->root)
+        ) {
+            $this->io->writeError('<warning>Cannot create cache directory ' . $this->root . ', or directory is not writable. Proceeding without cache</warning>');
+            $this->enabled = false;
         }
     }
 
@@ -86,6 +88,9 @@ class Cache
             try {
                 return file_put_contents($this->root . $file, $contents);
             } catch (\ErrorException $e) {
+                if ($this->io->isDebug()) {
+                    $this->io->writeError('<warning>Failed to write into cache: '.$e->getMessage().'</warning>');
+                }
                 if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
                     // Remove partial file.
                     unlink($this->root . $file);

+ 7 - 2
src/Composer/Command/ArchiveCommand.php

@@ -19,7 +19,7 @@ use Composer\Repository\CompositeRepository;
 use Composer\Script\ScriptEvents;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
-
+use Composer\Util\Filesystem;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
@@ -105,7 +105,12 @@ EOT
         }
 
         $io->writeError('<info>Creating the archive into "'.$dest.'".</info>');
-        $archiveManager->archive($package, $format, $dest);
+        $packagePath = $archiveManager->archive($package, $format, $dest);
+        $fs = new Filesystem;
+        $shortPath = $fs->findShortestPath(getcwd(), $packagePath, true);
+
+        $io->writeError('Created: ', false);
+        $io->write(strlen($shortPath) < strlen($packagePath) ? $shortPath : $packagePath);
 
         return 0;
     }

+ 25 - 28
src/Composer/Command/ConfigCommand.php

@@ -66,7 +66,7 @@ class ConfigCommand extends Command
                 new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'),
                 new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
                 new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
-                new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json', 'composer.json'),
+                new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json'),
                 new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
                 new InputArgument('setting-key', null, 'Setting key'),
                 new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
@@ -129,7 +129,7 @@ EOT
     {
         parent::initialize($input, $output);
 
-        if ($input->getOption('global') && 'composer.json' !== $input->getOption('file')) {
+        if ($input->getOption('global') && null !== $input->getOption('file')) {
             throw new \RuntimeException('--file and --global can not be combined');
         }
 
@@ -139,7 +139,7 @@ EOT
         // passed in a file to use
         $configFile = $input->getOption('global')
             ? ($this->config->get('home') . '/config.json')
-            : $input->getOption('file');
+            : ($input->getOption('file') ?: trim(getenv('COMPOSER')) ?: 'composer.json');
 
         // create global composer.json if this was invoked using `composer global config`
         if ($configFile === 'composer.json' && !file_exists($configFile) && realpath(getcwd()) === realpath($this->config->get('home'))) {
@@ -151,7 +151,7 @@ EOT
 
         $authConfigFile = $input->getOption('global')
             ? ($this->config->get('home') . '/auth.json')
-            : dirname(realpath($input->getOption('file'))) . '/auth.json';
+            : dirname(realpath($configFile)) . '/auth.json';
 
         $this->authConfigFile = new JsonFile($authConfigFile);
         $this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
@@ -238,17 +238,19 @@ EOT
             } elseif (strpos($settingKey, '.')) {
                 $bits = explode('.', $settingKey);
                 $data = $data['config'];
+                $match = false;
                 foreach ($bits as $bit) {
-                    if (isset($data[$bit])) {
-                        $data = $data[$bit];
-                    } elseif (isset($data[implode('.', $bits)])) {
-                        // last bit can contain domain names and such so try to join whatever is left if it exists
-                        $data = $data[implode('.', $bits)];
-                        break;
-                    } else {
-                        throw new \RuntimeException($settingKey.' is not defined');
+                    $key = isset($key) ? $key.'.'.$bit : $bit;
+                    $match = false;
+                    if (isset($data[$key])) {
+                        $match = true;
+                        $data = $data[$key];
+                        unset($key);
                     }
-                    array_shift($bits);
+                }
+
+                if (!$match) {
+                    throw new \RuntimeException($settingKey.' is not defined.');
                 }
 
                 $value = $data;
@@ -278,7 +280,7 @@ EOT
             'use-include-path' => array($booleanValidator, $booleanNormalizer),
             'preferred-install' => array(
                 function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); },
-                function ($val) { return $val; }
+                function ($val) { return $val; },
             ),
             'store-auths' => array(
                 function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); },
@@ -288,7 +290,7 @@ EOT
                     }
 
                     return $val !== 'false' && (bool) $val;
-                }
+                },
             ),
             'notify-on-install' => array($booleanValidator, $booleanNormalizer),
             'vendor-dir' => array('is_string', function ($val) { return $val; }),
@@ -301,7 +303,7 @@ EOT
             'cache-files-ttl' => array('is_numeric', 'intval'),
             'cache-files-maxsize' => array(
                 function ($val) { return preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $val) > 0; },
-                function ($val) { return $val; }
+                function ($val) { return $val; },
             ),
             'discard-changes' => array(
                 function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); },
@@ -311,7 +313,7 @@ EOT
                     }
 
                     return $val !== 'false' && (bool) $val;
-                }
+                },
             ),
             'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
             'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
@@ -336,7 +338,7 @@ EOT
                 },
                 function ($vals) {
                     return $vals;
-                }
+                },
             ),
             'github-domains' => array(
                 function ($vals) {
@@ -348,7 +350,7 @@ EOT
                 },
                 function ($vals) {
                     return $vals;
-                }
+                },
             ),
         );
 
@@ -464,6 +466,7 @@ EOT
     protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
     {
         $origK = $k;
+        $io = $this->getIO();
         foreach ($contents as $key => $value) {
             if ($k === null && !in_array($key, array('config', 'repositories'))) {
                 continue;
@@ -474,13 +477,7 @@ EOT
             if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
                 $k .= preg_replace('{^config\.}', '', $key . '.');
                 $this->listConfiguration($value, $rawVal, $output, $k);
-
-                if (substr_count($k, '.') > 1) {
-                    $k = str_split($k, strrpos($k, '.', -2));
-                    $k = $k[0] . '.';
-                } else {
-                    $k = $origK;
-                }
+                $k = $origK;
 
                 continue;
             }
@@ -498,9 +495,9 @@ EOT
             }
 
             if (is_string($rawVal) && $rawVal != $value) {
-                $this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
+                $io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
             } else {
-                $this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
+                $io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
             }
         }
     }

+ 8 - 7
src/Composer/Command/CreateProjectCommand.php

@@ -100,16 +100,17 @@ EOT
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $config = Factory::createConfig();
+        $io = $this->getIO();
 
         $this->updatePreferredOptions($config, $input, $preferSource, $preferDist, true);
 
         if ($input->getOption('no-custom-installers')) {
-            $this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
+            $io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
             $input->setOption('no-plugins', true);
         }
 
         return $this->installProject(
-            $this->getIO(),
+            $io,
             $config,
             $input->getArgument('package'),
             $input->getArgument('directory'),
@@ -290,15 +291,15 @@ EOT
 
         // handler Ctrl+C for unix-like systems
         if (function_exists('pcntl_signal')) {
-            declare(ticks = 100);
-            pcntl_signal(SIGINT, function() use ($directory) {
+            declare (ticks = 100);
+            pcntl_signal(SIGINT, function () use ($directory) {
                 $fs = new Filesystem();
                 $fs->removeDirectory($directory);
                 exit(130);
             });
         }
 
-        $io->writeError('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
+        $io->writeError('<info>Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')</info>');
 
         if ($disablePlugins) {
             $io->writeError('<info>Plugins have been disabled.</info>');
@@ -346,8 +347,8 @@ EOT
      * Updated preferSource or preferDist based on the preferredInstall config option
      * @param Config         $config
      * @param InputInterface $input
-     * @param boolean        $preferSource
-     * @param boolean        $preferDist
+     * @param bool           $preferSource
+     * @param bool           $preferDist
      */
     protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist, $keepVcsRequiresPreferSource = false)
     {

+ 3 - 2
src/Composer/Command/DependsCommand.php

@@ -81,6 +81,7 @@ EOT
 
         $messages = array();
         $outputPackages = array();
+        $io = $this->getIO();
         foreach ($repo->getPackages() as $package) {
             foreach ($types as $type) {
                 foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
@@ -96,9 +97,9 @@ EOT
 
         if ($messages) {
             sort($messages);
-            $this->getIO()->write($messages);
+            $io->write($messages);
         } else {
-            $this->getIO()->writeError('<info>There is no installed package depending on "'.$needle.'".</info>');
+            $io->writeError('<info>There is no installed package depending on "'.$needle.'".</info>');
         }
     }
 }

+ 27 - 25
src/Composer/Command/DiagnoseCommand.php

@@ -57,12 +57,13 @@ EOT
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $composer = $this->getComposer(false);
+        $io = $this->getIO();
 
         if ($composer) {
             $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
             $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
 
-            $this->getIO()->write('Checking composer.json: ', false);
+            $io->write('Checking composer.json: ', false);
             $this->outputResult($this->checkComposerSchema());
         }
 
@@ -72,44 +73,44 @@ EOT
             $config = Factory::createConfig();
         }
 
-        $this->rfs = new RemoteFilesystem($this->getIO(), $config);
-        $this->process = new ProcessExecutor($this->getIO());
+        $this->rfs = new RemoteFilesystem($io, $config);
+        $this->process = new ProcessExecutor($io);
 
-        $this->getIO()->write('Checking platform settings: ', false);
+        $io->write('Checking platform settings: ', false);
         $this->outputResult($this->checkPlatform());
 
-        $this->getIO()->write('Checking git settings: ', false);
+        $io->write('Checking git settings: ', false);
         $this->outputResult($this->checkGit());
 
-        $this->getIO()->write('Checking http connectivity to packagist: ', false);
+        $io->write('Checking http connectivity to packagist: ', false);
         $this->outputResult($this->checkHttp('http'));
 
-        $this->getIO()->write('Checking https connectivity to packagist: ', false);
+        $io->write('Checking https connectivity to packagist: ', false);
         $this->outputResult($this->checkHttp('https'));
 
         $opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
         if (!empty($opts['http']['proxy'])) {
-            $this->getIO()->write('Checking HTTP proxy: ', false);
+            $io->write('Checking HTTP proxy: ', false);
             $this->outputResult($this->checkHttpProxy());
-            $this->getIO()->write('Checking HTTP proxy support for request_fulluri: ', false);
+            $io->write('Checking HTTP proxy support for request_fulluri: ', false);
             $this->outputResult($this->checkHttpProxyFullUriRequestParam());
-            $this->getIO()->write('Checking HTTPS proxy support for request_fulluri: ', false);
+            $io->write('Checking HTTPS proxy support for request_fulluri: ', false);
             $this->outputResult($this->checkHttpsProxyFullUriRequestParam());
         }
 
         if ($oauth = $config->get('github-oauth')) {
             foreach ($oauth as $domain => $token) {
-                $this->getIO()->write('Checking '.$domain.' oauth access: ', false);
+                $io->write('Checking '.$domain.' oauth access: ', false);
                 $this->outputResult($this->checkGithubOauth($domain, $token));
             }
         } else {
-            $this->getIO()->write('Checking github.com rate limit: ', false);
+            $io->write('Checking github.com rate limit: ', false);
             try {
                 $rate = $this->getGithubRateLimit('github.com');
                 $this->outputResult(true);
                 if (10 > $rate['remaining']) {
-                    $this->getIO()->write('<warning>WARNING</warning>');
-                    $this->getIO()->write(sprintf(
+                    $io->write('<warning>WARNING</warning>');
+                    $io->write(sprintf(
                         '<comment>Github has a rate limit on their API. '
                         . 'You currently have <options=bold>%u</options=bold> '
                         . 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
@@ -128,10 +129,10 @@ EOT
             }
         }
 
-        $this->getIO()->write('Checking disk free space: ', false);
+        $io->write('Checking disk free space: ', false);
         $this->outputResult($this->checkDiskSpace($config));
 
-        $this->getIO()->write('Checking composer version: ', false);
+        $io->write('Checking composer version: ', false);
         $this->outputResult($this->checkVersion());
 
         return $this->failures;
@@ -263,7 +264,7 @@ EOT
             $url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos';
 
             return $this->rfs->getContents($domain, $url, false, array(
-                'retry-auth-failure' => false
+                'retry-auth-failure' => false,
             )) ? true : 'Unexpected error';
         } catch (\Exception $e) {
             if ($e instanceof TransportException && $e->getCode() === 401) {
@@ -275,10 +276,10 @@ EOT
     }
 
     /**
-     * @param string $domain
-     * @param string $token
-     * @return array
+     * @param  string             $domain
+     * @param  string             $token
      * @throws TransportException
+     * @return array
      */
     private function getGithubRateLimit($domain, $token = null)
     {
@@ -295,7 +296,7 @@ EOT
 
     private function checkDiskSpace($config)
     {
-        $minSpaceFree = 1024*1024;
+        $minSpaceFree = 1024 * 1024;
         if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
             || (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
         ) {
@@ -322,15 +323,16 @@ EOT
      */
     private function outputResult($result)
     {
+        $io = $this->getIO();
         if (true === $result) {
-            $this->getIO()->write('<info>OK</info>');
+            $io->write('<info>OK</info>');
         } else {
             $this->failures++;
-            $this->getIO()->write('<error>FAIL</error>');
+            $io->write('<error>FAIL</error>');
             if ($result instanceof \Exception) {
-                $this->getIO()->write('['.get_class($result).'] '.$result->getMessage());
+                $io->write('['.get_class($result).'] '.$result->getMessage());
             } elseif ($result) {
-                $this->getIO()->write(trim($result));
+                $io->write(trim($result));
             }
         }
     }

+ 5 - 2
src/Composer/Command/DumpAutoloadCommand.php

@@ -31,6 +31,7 @@ class DumpAutoloadCommand extends Command
             ->setDescription('Dumps the autoloader')
             ->setDefinition(array(
                 new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'),
+                new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'),
                 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
             ))
             ->setHelp(<<<EOT
@@ -52,9 +53,10 @@ EOT
         $package = $composer->getPackage();
         $config = $composer->getConfig();
 
-        $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
+        $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
+        $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
 
-        if ($optimize) {
+        if ($optimize || $authoritative) {
             $this->getIO()->writeError('<info>Generating optimized autoload files</info>');
         } else {
             $this->getIO()->writeError('<info>Generating autoload files</info>');
@@ -62,6 +64,7 @@ EOT
 
         $generator = $composer->getAutoloadGenerator();
         $generator->setDevMode(!$input->getOption('no-dev'));
+        $generator->setClassMapAuthoritative($authoritative);
         $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
     }
 }

+ 3 - 3
src/Composer/Command/HomeCommand.php

@@ -14,7 +14,6 @@ namespace Composer\Command;
 
 use Composer\Factory;
 use Composer\Package\CompletePackageInterface;
-use Composer\Repository\CompositeRepository;
 use Composer\Repository\RepositoryInterface;
 use Composer\Repository\ArrayRepository;
 use Composer\Util\ProcessExecutor;
@@ -58,6 +57,7 @@ EOT
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $repos = $this->initializeRepos();
+        $io = $this->getIO();
         $return = 0;
 
         foreach ($input->getArgument('packages') as $packageName) {
@@ -75,12 +75,12 @@ EOT
 
             if (!$packageExists) {
                 $return = 1;
-                $this->getIO()->writeError('<warning>Package '.$packageName.' not found</warning>');
+                $io->writeError('<warning>Package '.$packageName.' not found</warning>');
             }
 
             if (!$handled) {
                 $return = 1;
-                $this->getIO()->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
+                $io->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
             }
         }
 

+ 33 - 30
src/Composer/Command/InitCommand.php

@@ -16,10 +16,10 @@ use Composer\DependencyResolver\Pool;
 use Composer\Json\JsonFile;
 use Composer\Factory;
 use Composer\Package\BasePackage;
+use Composer\Package\Version\VersionParser;
 use Composer\Package\Version\VersionSelector;
 use Composer\Repository\CompositeRepository;
 use Composer\Repository\PlatformRepository;
-use Composer\Package\Version\VersionParser;
 use Composer\Util\ProcessExecutor;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
@@ -106,11 +106,12 @@ EOT
 
         $file = new JsonFile('composer.json');
         $json = $file->encode($options);
+        $io = $this->getIO();
 
         if ($input->isInteractive()) {
-            $this->getIO()->writeError(array('', $json, ''));
-            if (!$this->getIO()->askConfirmation('Do you confirm generation [<comment>yes</comment>]? ', true)) {
-                $this->getIO()->writeError('<error>Command aborted</error>');
+            $io->writeError(array('', $json, ''));
+            if (!$io->askConfirmation('Do you confirm generation [<comment>yes</comment>]? ', true)) {
+                $io->writeError('<error>Command aborted</error>');
 
                 return 1;
             }
@@ -128,7 +129,7 @@ EOT
             if (!$this->hasVendorIgnore($ignoreFile)) {
                 $question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]? ';
 
-                if ($this->getIO()->askConfirmation($question, true)) {
+                if ($io->askConfirmation($question, true)) {
                     $this->addVendorIgnore($ignoreFile);
                 }
             }
@@ -141,17 +142,17 @@ EOT
     protected function interact(InputInterface $input, OutputInterface $output)
     {
         $git = $this->getGitConfig();
-
+        $io = $this->getIO();
         $formatter = $this->getHelperSet()->get('formatter');
 
-        $this->getIO()->writeError(array(
+        $io->writeError(array(
             '',
             $formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
-            ''
+            '',
         ));
 
         // namespace
-        $this->getIO()->writeError(array(
+        $io->writeError(array(
             '',
             'This command will guide you through creating your composer.json config.',
             '',
@@ -181,7 +182,7 @@ EOT
             }
         }
 
-        $name = $this->getIO()->askAndValidate(
+        $name = $io->askAndValidate(
             'Package name (<vendor>/<name>) [<comment>'.$name.'</comment>]: ',
             function ($value) use ($name) {
                 if (null === $value) {
@@ -202,7 +203,7 @@ EOT
         $input->setOption('name', $name);
 
         $description = $input->getOption('description') ?: false;
-        $description = $this->getIO()->ask(
+        $description = $io->ask(
             'Description [<comment>'.$description.'</comment>]: ',
             $description
         );
@@ -215,7 +216,7 @@ EOT
         }
 
         $self = $this;
-        $author = $this->getIO()->askAndValidate(
+        $author = $io->askAndValidate(
             'Author [<comment>'.$author.'</comment>]: ',
             function ($value) use ($self, $author) {
                 $value = $value ?: $author;
@@ -229,7 +230,7 @@ EOT
         $input->setOption('author', $author);
 
         $minimumStability = $input->getOption('stability') ?: null;
-        $minimumStability = $this->getIO()->askAndValidate(
+        $minimumStability = $io->askAndValidate(
             'Minimum Stability [<comment>'.$minimumStability.'</comment>]: ',
             function ($value) use ($self, $minimumStability) {
                 if (null === $value) {
@@ -251,31 +252,31 @@ EOT
         $input->setOption('stability', $minimumStability);
 
         $type = $input->getOption('type') ?: false;
-        $type = $this->getIO()->ask(
+        $type = $io->ask(
             'Package Type [<comment>'.$type.'</comment>]: ',
             $type
         );
         $input->setOption('type', $type);
 
         $license = $input->getOption('license') ?: false;
-        $license = $this->getIO()->ask(
+        $license = $io->ask(
             'License [<comment>'.$license.'</comment>]: ',
             $license
         );
         $input->setOption('license', $license);
 
-        $this->getIO()->writeError(array('', 'Define your dependencies.', ''));
+        $io->writeError(array('', 'Define your dependencies.', ''));
 
         $question = 'Would you like to define your dependencies (require) interactively [<comment>yes</comment>]? ';
         $requirements = array();
-        if ($this->getIO()->askConfirmation($question, true)) {
+        if ($io->askConfirmation($question, true)) {
             $requirements = $this->determineRequirements($input, $output, $input->getOption('require'));
         }
         $input->setOption('require', $requirements);
 
         $question = 'Would you like to define your dev dependencies (require-dev) interactively [<comment>yes</comment>]? ';
         $devRequirements = array();
-        if ($this->getIO()->askConfirmation($question, true)) {
+        if ($io->askConfirmation($question, true)) {
             $devRequirements = $this->determineRequirements($input, $output, $input->getOption('require-dev'));
         }
         $input->setOption('require-dev', $devRequirements);
@@ -283,7 +284,7 @@ EOT
 
     /**
      * @private
-     * @param string $author
+     * @param  string $author
      * @return array
      */
     public function parseAuthorString($author)
@@ -292,7 +293,7 @@ EOT
             if ($this->isValidEmail($match['email'])) {
                 return array(
                     'name'  => trim($match['name']),
-                    'email' => $match['email']
+                    'email' => $match['email'],
                 );
             }
         }
@@ -325,6 +326,7 @@ EOT
         if ($requires) {
             $requires = $this->normalizeRequirements($requires);
             $result = array();
+            $io = $this->getIO();
 
             foreach ($requires as $requirement) {
                 if (!isset($requirement['version'])) {
@@ -332,7 +334,7 @@ EOT
                     $version = $this->findBestVersionForPackage($input, $requirement['name']);
                     $requirement['version'] = $version;
 
-                    $this->getIO()->writeError(sprintf(
+                    $io->writeError(sprintf(
                         'Using version <info>%s</info> for <info>%s</info>',
                         $requirement['version'],
                         $requirement['name']
@@ -346,7 +348,8 @@ EOT
         }
 
         $versionParser = new VersionParser();
-        while (null !== $package = $this->getIO()->ask('Search for a package: ')) {
+        $io = $this->getIO();
+        while (null !== $package = $io->ask('Search for a package: ')) {
             $matches = $this->findPackages($package);
 
             if (count($matches)) {
@@ -362,14 +365,14 @@ EOT
 
                 // no match, prompt which to pick
                 if (!$exactMatch) {
-                    $this->getIO()->writeError(array(
+                    $io->writeError(array(
                         '',
                         sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
-                        ''
+                        '',
                     ));
 
-                    $this->getIO()->writeError($choices);
-                    $this->getIO()->writeError('');
+                    $io->writeError($choices);
+                    $io->writeError('');
 
                     $validator = function ($selection) use ($matches, $versionParser) {
                         if ('' === $selection) {
@@ -399,7 +402,7 @@ EOT
                         throw new \Exception('Not a valid selection');
                     };
 
-                    $package = $this->getIO()->askAndValidate(
+                    $package = $io->askAndValidate(
                         'Enter package # to add, or the complete package name if it is not listed: ',
                         $validator,
                         3,
@@ -415,7 +418,7 @@ EOT
                         return $input ?: false;
                     };
 
-                    $constraint = $this->getIO()->askAndValidate(
+                    $constraint = $io->askAndValidate(
                         'Enter the version constraint to require (or leave blank to use the latest version): ',
                         $validator,
                         3,
@@ -425,7 +428,7 @@ EOT
                     if (false === $constraint) {
                         $constraint = $this->findBestVersionForPackage($input, $package);
 
-                        $this->getIO()->writeError(sprintf(
+                        $io->writeError(sprintf(
                             'Using version <info>%s</info> for <info>%s</info>',
                             $constraint,
                             $package
@@ -588,8 +591,8 @@ EOT
      *
      * @param  InputInterface            $input
      * @param  string                    $name
-     * @return string
      * @throws \InvalidArgumentException
+     * @return string
      */
     private function findBestVersionForPackage(InputInterface $input, $name)
     {

+ 8 - 5
src/Composer/Command/InstallCommand.php

@@ -46,6 +46,7 @@ class InstallCommand extends Command
                 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
                 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
                 new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
+                new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
                 new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
             ))
@@ -64,24 +65,24 @@ EOT
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
+        $io = $this->getIO();
         if ($args = $input->getArgument('packages')) {
-            $this->getIO()->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
+            $io->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
 
             return 1;
         }
 
         if ($input->getOption('no-custom-installers')) {
-            $this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
+            $io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
             $input->setOption('no-plugins', true);
         }
 
         if ($input->getOption('dev')) {
-            $this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
+            $io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
         }
 
         $composer = $this->getComposer(true, $input->getOption('no-plugins'));
         $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
-        $io = $this->getIO();
 
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
         $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@@ -110,7 +111,8 @@ EOT
             $preferDist = $input->getOption('prefer-dist');
         }
 
-        $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
+        $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
+        $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
 
         $install
             ->setDryRun($input->getOption('dry-run'))
@@ -121,6 +123,7 @@ EOT
             ->setDumpAutoloader(!$input->getOption('no-autoloader'))
             ->setRunScripts(!$input->getOption('no-scripts'))
             ->setOptimizeAutoloader($optimize)
+            ->setClassMapAuthoritative($authoritative)
             ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
         ;
 

+ 10 - 12
src/Composer/Command/LicensesCommand.php

@@ -13,7 +13,6 @@
 namespace Composer\Command;
 
 use Composer\Json\JsonFile;
-use Composer\Package\Version\VersionParser;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
 use Composer\Package\PackageInterface;
@@ -56,8 +55,6 @@ EOT
         $root = $composer->getPackage();
         $repo = $composer->getRepositoryManager()->getLocalRepository();
 
-        $versionParser = new VersionParser;
-
         if ($input->getOption('no-dev')) {
             $packages = $this->filterRequiredPackages($repo, $root);
         } else {
@@ -65,14 +62,15 @@ EOT
         }
 
         ksort($packages);
+        $io = $this->getIO();
 
         switch ($format = $input->getOption('format')) {
             case 'text':
-                $this->getIO()->write('Name: <comment>'.$root->getPrettyName().'</comment>');
-                $this->getIO()->write('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
-                $this->getIO()->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
-                $this->getIO()->write('Dependencies:');
-                $this->getIO()->write('');
+                $io->write('Name: <comment>'.$root->getPrettyName().'</comment>');
+                $io->write('Version: <comment>'.$root->getFullPrettyVersion().'</comment>');
+                $io->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
+                $io->write('Dependencies:');
+                $io->write('');
 
                 $table = new Table($output);
                 $table->setStyle('compact');
@@ -82,7 +80,7 @@ EOT
                 foreach ($packages as $package) {
                     $table->addRow(array(
                         $package->getPrettyName(),
-                        $versionParser->formatVersion($package),
+                        $package->getFullPrettyVersion(),
                         implode(', ', $package->getLicense()) ?: 'none',
                     ));
                 }
@@ -92,14 +90,14 @@ EOT
             case 'json':
                 foreach ($packages as $package) {
                     $dependencies[$package->getPrettyName()] = array(
-                        'version' => $versionParser->formatVersion($package),
+                        'version' => $package->getFullPrettyVersion(),
                         'license' => $package->getLicense(),
                     );
                 }
 
-                $this->getIO()->write(JsonFile::encode(array(
+                $io->write(JsonFile::encode(array(
                     'name'         => $root->getPrettyName(),
-                    'version'      => $versionParser->formatVersion($root),
+                    'version'      => $root->getFullPrettyVersion(),
                     'license'      => $root->getLicense(),
                     'dependencies' => $dependencies,
                 )));

+ 13 - 6
src/Composer/Command/RemoveCommand.php

@@ -42,6 +42,8 @@ class RemoveCommand extends Command
                 new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
                 new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
+                new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
+                new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
             ))
             ->setHelp(<<<EOT
 The <info>remove</info> command removes a package from the current
@@ -68,19 +70,20 @@ EOT
 
         $type = $input->getOption('dev') ? 'require-dev' : 'require';
         $altType = !$input->getOption('dev') ? 'require-dev' : 'require';
+        $io = $this->getIO();
 
         foreach ($packages as $package) {
             if (isset($composer[$type][$package])) {
                 $json->removeLink($type, $package);
             } elseif (isset($composer[$altType][$package])) {
-                $this->getIO()->writeError('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
-                if ($this->getIO()->isInteractive()) {
-                    if ($this->getIO()->askConfirmation('Do you want to remove it from '.$altType.' [<comment>yes</comment>]? ', true)) {
+                $io->writeError('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
+                if ($io->isInteractive()) {
+                    if ($io->askConfirmation('Do you want to remove it from '.$altType.' [<comment>yes</comment>]? ', true)) {
                         $json->removeLink($altType, $package);
                     }
                 }
             } else {
-                $this->getIO()->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
+                $io->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
             }
         }
 
@@ -91,7 +94,6 @@ EOT
         // Update packages
         $composer = $this->getComposer();
         $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
-        $io = $this->getIO();
 
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
         $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@@ -99,9 +101,14 @@ EOT
         $install = Installer::create($io, $composer);
 
         $updateDevMode = !$input->getOption('update-no-dev');
+        $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
+        $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
+
         $install
             ->setVerbose($input->getOption('verbose'))
             ->setDevMode($updateDevMode)
+            ->setOptimizeAutoloader($optimize)
+            ->setClassMapAuthoritative($authoritative)
             ->setUpdate(true)
             ->setUpdateWhitelist($packages)
             ->setWhitelistDependencies($input->getOption('update-with-dependencies'))
@@ -110,7 +117,7 @@ EOT
 
         $status = $install->run();
         if ($status !== 0) {
-            $this->getIO()->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
+            $io->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
             file_put_contents($jsonFile->getPath(), $composerBackup);
         }
 

+ 14 - 8
src/Composer/Command/RequireCommand.php

@@ -20,7 +20,7 @@ use Composer\Factory;
 use Composer\Installer;
 use Composer\Json\JsonFile;
 use Composer\Json\JsonManipulator;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\VersionParser;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
 use Composer\Repository\CompositeRepository;
@@ -48,6 +48,8 @@ class RequireCommand extends InitCommand
                 new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
                 new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
+                new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
+                new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
             ))
             ->setHelp(<<<EOT
 The require command adds required packages to your composer.json and installs them.
@@ -64,20 +66,21 @@ EOT
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $file = Factory::getComposerFile();
+        $io = $this->getIO();
 
         $newlyCreated = !file_exists($file);
         if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
-            $this->getIO()->writeError('<error>'.$file.' could not be created.</error>');
+            $io->writeError('<error>'.$file.' could not be created.</error>');
 
             return 1;
         }
         if (!is_readable($file)) {
-            $this->getIO()->writeError('<error>'.$file.' is not readable.</error>');
+            $io->writeError('<error>'.$file.' is not readable.</error>');
 
             return 1;
         }
         if (!is_writable($file)) {
-            $this->getIO()->writeError('<error>'.$file.' is not writable.</error>');
+            $io->writeError('<error>'.$file.' is not writable.</error>');
 
             return 1;
         }
@@ -126,18 +129,19 @@ EOT
             $json->write($composerDefinition);
         }
 
-        $this->getIO()->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
+        $io->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
 
         if ($input->getOption('no-update')) {
             return 0;
         }
         $updateDevMode = !$input->getOption('update-no-dev');
+        $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
+        $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
 
         // Update packages
         $this->resetComposer();
         $composer = $this->getComposer();
         $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
-        $io = $this->getIO();
 
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
         $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@@ -149,6 +153,8 @@ EOT
             ->setPreferSource($input->getOption('prefer-source'))
             ->setPreferDist($input->getOption('prefer-dist'))
             ->setDevMode($updateDevMode)
+            ->setOptimizeAutoloader($optimize)
+            ->setClassMapAuthoritative($authoritative)
             ->setUpdate(true)
             ->setUpdateWhitelist(array_keys($requirements))
             ->setWhitelistDependencies($input->getOption('update-with-dependencies'))
@@ -158,10 +164,10 @@ EOT
         $status = $install->run();
         if ($status !== 0) {
             if ($newlyCreated) {
-                $this->getIO()->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
+                $io->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
                 unlink($json->getPath());
             } else {
-                $this->getIO()->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
+                $io->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
                 file_put_contents($json->getPath(), $composerBackup);
             }
         }

+ 3 - 2
src/Composer/Command/RunScriptCommand.php

@@ -104,9 +104,10 @@ EOT
             return 0;
         }
 
-        $this->getIO()->writeError('<info>scripts:</info>');
+        $io = $this->getIO();
+        $io->writeError('<info>scripts:</info>');
         foreach ($scripts as $name => $script) {
-            $this->getIO()->write('  ' . $name);
+            $io->write('  ' . $name);
         }
 
         return 0;

+ 4 - 3
src/Composer/Command/SearchCommand.php

@@ -56,13 +56,14 @@ EOT
     {
         // init repos
         $platformRepo = new PlatformRepository;
+        $io = $this->getIO();
         if ($composer = $this->getComposer(false)) {
             $localRepo = $composer->getRepositoryManager()->getLocalRepository();
             $installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
             $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
         } else {
-            $defaultRepos = Factory::createDefaultRepositories($this->getIO());
-            $this->getIO()->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
+            $defaultRepos = Factory::createDefaultRepositories($io);
+            $io->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
             $installedRepo = $platformRepo;
             $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
         }
@@ -78,7 +79,7 @@ EOT
         $results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
 
         foreach ($results as $result) {
-            $this->getIO()->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
+            $io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
         }
     }
 }

+ 14 - 12
src/Composer/Command/SelfUpdateCommand.php

@@ -60,7 +60,8 @@ EOT
     {
         $baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
         $config = Factory::createConfig();
-        $remoteFilesystem = new RemoteFilesystem($this->getIO(), $config);
+        $io = $this->getIO();
+        $remoteFilesystem = new RemoteFilesystem($io, $config);
         $cacheDir = $config->get('cache-dir');
         $rollbackDir = $config->get('data-dir');
         $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
@@ -84,13 +85,13 @@ EOT
         $updateVersion = $input->getArgument('version') ?: $latestVersion;
 
         if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
-            $this->getIO()->writeError('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
+            $io->writeError('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
 
             return 1;
         }
 
         if (Composer::VERSION === $updateVersion) {
-            $this->getIO()->writeError('<info>You are already using composer version '.$updateVersion.'.</info>');
+            $io->writeError('<info>You are already using composer version '.$updateVersion.'.</info>');
 
             return 0;
         }
@@ -104,11 +105,11 @@ EOT
             self::OLD_INSTALL_EXT
         );
 
-        $this->getIO()->writeError(sprintf("Updating to version <info>%s</info>.", $updateVersion));
+        $io->writeError(sprintf("Updating to version <info>%s</info>.", $updateVersion));
         $remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar");
         $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
         if (!file_exists($tempFilename)) {
-            $this->getIO()->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
+            $io->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
 
             return 1;
         }
@@ -120,22 +121,22 @@ EOT
             $fs = new Filesystem;
             foreach ($finder as $file) {
                 $file = (string) $file;
-                $this->getIO()->writeError('<info>Removing: '.$file.'</info>');
+                $io->writeError('<info>Removing: '.$file.'</info>');
                 $fs->remove($file);
             }
         }
 
         if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
-            $this->getIO()->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
-            $this->getIO()->writeError('<error>Please re-run the self-update command to try again.</error>');
+            $io->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
+            $io->writeError('<error>Please re-run the self-update command to try again.</error>');
 
             return 1;
         }
 
         if (file_exists($backupFile)) {
-            $this->getIO()->writeError('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
+            $io->writeError('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
         } else {
-            $this->getIO()->writeError('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
+            $io->writeError('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
         }
     }
 
@@ -160,9 +161,10 @@ EOT
         }
 
         $oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
-        $this->getIO()->writeError(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
+        $io = $this->getIO();
+        $io->writeError(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
         if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
-            $this->getIO()->writeError('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
+            $io->writeError('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
 
             return 1;
         }

+ 41 - 37
src/Composer/Command/ShowCommand.php

@@ -16,7 +16,7 @@ use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\DefaultPolicy;
 use Composer\Factory;
 use Composer\Package\CompletePackageInterface;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\VersionParser;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
 use Symfony\Component\Console\Input\InputInterface;
@@ -28,7 +28,7 @@ use Composer\Repository\CompositeRepository;
 use Composer\Repository\ComposerRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryInterface;
-use Composer\Util\SpdxLicense;
+use Composer\Spdx\SpdxLicenses;
 
 /**
  * @author Robert Schönthal <seroscho@googlemail.com>
@@ -71,6 +71,7 @@ EOT
         $platformRepo = new PlatformRepository;
 
         $composer = $this->getComposer(false);
+        $io = $this->getIO();
         if ($input->getOption('self')) {
             $package = $this->getComposer()->getPackage();
             $repos = $installedRepo = new ArrayRepository(array($package));
@@ -83,17 +84,17 @@ EOT
             if ($composer) {
                 $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
             } else {
-                $defaultRepos = Factory::createDefaultRepositories($this->getIO());
+                $defaultRepos = Factory::createDefaultRepositories($io);
                 $repos = new CompositeRepository($defaultRepos);
-                $this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
+                $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
             }
         } elseif ($composer) {
             $localRepo = $composer->getRepositoryManager()->getLocalRepository();
             $installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
             $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
         } else {
-            $defaultRepos = Factory::createDefaultRepositories($this->getIO());
-            $this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
+            $defaultRepos = Factory::createDefaultRepositories($io);
+            $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
             $installedRepo = $platformRepo;
             $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
         }
@@ -120,9 +121,9 @@ EOT
             $this->printLinks($package, 'requires');
             $this->printLinks($package, 'devRequires', 'requires (dev)');
             if ($package->getSuggests()) {
-                $this->getIO()->write("\n<info>suggests</info>");
+                $io->write("\n<info>suggests</info>");
                 foreach ($package->getSuggests() as $suggested => $reason) {
-                    $this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>');
+                    $io->write($suggested . ' <comment>' . $reason . '</comment>');
                 }
             }
             $this->printLinks($package, 'provides');
@@ -173,7 +174,7 @@ EOT
         foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
             if (isset($packages[$type])) {
                 if ($tree) {
-                    $this->getIO()->write($type);
+                    $io->write($type);
                 }
                 ksort($packages[$type]);
 
@@ -181,7 +182,7 @@ EOT
                 foreach ($packages[$type] as $package) {
                     if (is_object($package)) {
                         $nameLength = max($nameLength, strlen($package->getPrettyName()));
-                        $versionLength = max($versionLength, strlen($this->versionParser->formatVersion($package)));
+                        $versionLength = max($versionLength, strlen($package->getFullPrettyVersion()));
                     } else {
                         $nameLength = max($nameLength, $package);
                     }
@@ -197,7 +198,7 @@ EOT
                 }
 
                 if ($input->getOption('path') && null === $composer) {
-                    $this->getIO()->writeError('No composer.json found in the current directory, disabling "path" option');
+                    $io->writeError('No composer.json found in the current directory, disabling "path" option');
                     $input->setOption('path', false);
                 }
 
@@ -209,7 +210,7 @@ EOT
                         $output->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
 
                         if ($writeVersion) {
-                            $output->write(' ' . str_pad($this->versionParser->formatVersion($package), $versionLength, ' '), false);
+                            $output->write(' ' . str_pad($package->getFullPrettyVersion(), $versionLength, ' '), false);
                         }
 
                         if ($writeDescription) {
@@ -228,10 +229,10 @@ EOT
                     } else {
                         $output->write($indent . $package);
                     }
-                    $this->getIO()->write('');
+                    $io->write('');
                 }
                 if ($tree) {
-                    $this->getIO()->write('');
+                    $io->write('');
                 }
             }
         }
@@ -244,8 +245,8 @@ EOT
      * @param  RepositoryInterface       $repos
      * @param  string                    $name
      * @param  string                    $version
-     * @return array                     array(CompletePackageInterface, array of versions)
      * @throws \InvalidArgumentException
+     * @return array                     array(CompletePackageInterface, array of versions)
      */
     protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null)
     {
@@ -291,53 +292,54 @@ EOT
      */
     protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
     {
-        $this->getIO()->write('<info>name</info>     : ' . $package->getPrettyName());
-        $this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
-        $this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
+        $io = $this->getIO();
+        $io->write('<info>name</info>     : ' . $package->getPrettyName());
+        $io->write('<info>descrip.</info> : ' . $package->getDescription());
+        $io->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
         $this->printVersions($package, $versions, $installedRepo);
-        $this->getIO()->write('<info>type</info>     : ' . $package->getType());
+        $io->write('<info>type</info>     : ' . $package->getType());
         $this->printLicenses($package);
-        $this->getIO()->write('<info>source</info>   : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
-        $this->getIO()->write('<info>dist</info>     : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
-        $this->getIO()->write('<info>names</info>    : ' . implode(', ', $package->getNames()));
+        $io->write('<info>source</info>   : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
+        $io->write('<info>dist</info>     : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
+        $io->write('<info>names</info>    : ' . implode(', ', $package->getNames()));
 
         if ($package->isAbandoned()) {
             $replacement = ($package->getReplacementPackage() !== null)
                 ? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
                 : null;
 
-            $this->getIO()->writeError(
+            $io->writeError(
                 sprintf('<warning>Attention: This package is abandoned and no longer maintained.%s</warning>', $replacement)
             );
         }
 
         if ($package->getSupport()) {
-            $this->getIO()->write("\n<info>support</info>");
+            $io->write("\n<info>support</info>");
             foreach ($package->getSupport() as $type => $value) {
-                $this->getIO()->write('<comment>' . $type . '</comment> : '.$value);
+                $io->write('<comment>' . $type . '</comment> : '.$value);
             }
         }
 
         if ($package->getAutoload()) {
-            $this->getIO()->write("\n<info>autoload</info>");
+            $io->write("\n<info>autoload</info>");
             foreach ($package->getAutoload() as $type => $autoloads) {
-                $this->getIO()->write('<comment>' . $type . '</comment>');
+                $io->write('<comment>' . $type . '</comment>');
 
                 if ($type === 'psr-0') {
                     foreach ($autoloads as $name => $path) {
-                        $this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
+                        $io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
                     }
                 } elseif ($type === 'psr-4') {
                     foreach ($autoloads as $name => $path) {
-                        $this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
+                        $io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
                     }
                 } elseif ($type === 'classmap') {
-                    $this->getIO()->write(implode(', ', $autoloads));
+                    $io->write(implode(', ', $autoloads));
                 }
             }
             if ($package->getIncludePaths()) {
-                $this->getIO()->write('<comment>include-path</comment>');
-                $this->getIO()->write(implode(', ', $package->getIncludePaths()));
+                $io->write('<comment>include-path</comment>');
+                $io->write(implode(', ', $package->getIncludePaths()));
             }
         }
     }
@@ -374,11 +376,12 @@ EOT
     protected function printLinks(CompletePackageInterface $package, $linkType, $title = null)
     {
         $title = $title ?: $linkType;
+        $io = $this->getIO();
         if ($links = $package->{'get'.ucfirst($linkType)}()) {
-            $this->getIO()->write("\n<info>" . $title . "</info>");
+            $io->write("\n<info>" . $title . "</info>");
 
             foreach ($links as $link) {
-                $this->getIO()->write($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
+                $io->write($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
             }
         }
     }
@@ -390,12 +393,13 @@ EOT
      */
     protected function printLicenses(CompletePackageInterface $package)
     {
-        $spdxLicense = new SpdxLicense;
+        $spdxLicenses = new SpdxLicenses();
 
         $licenses = $package->getLicense();
+        $io = $this->getIO();
 
         foreach ($licenses as $licenseId) {
-            $license = $spdxLicense->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
+            $license = $spdxLicenses->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
 
             if (!$license) {
                 $out = $licenseId;
@@ -408,7 +412,7 @@ EOT
                 }
             }
 
-            $this->getIO()->write('<info>license</info>  : ' . $out);
+            $io->write('<info>license</info>  : ' . $out);
         }
     }
 }

+ 11 - 6
src/Composer/Command/StatusCommand.php

@@ -60,6 +60,7 @@ EOT
         $composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
 
         $errors = array();
+        $io = $this->getIO();
 
         // list packages
         foreach ($installedRepo->getPackages() as $package) {
@@ -68,6 +69,10 @@ EOT
             if ($downloader instanceof ChangeReportInterface) {
                 $targetDir = $im->getInstallPath($package);
 
+                if (is_link($targetDir)) {
+                    $errors[$targetDir] = $targetDir . ' is a symbolic link.';
+                }
+
                 if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
                     $errors[$targetDir] = $changes;
                 }
@@ -76,9 +81,9 @@ EOT
 
         // output errors/warnings
         if (!$errors) {
-            $this->getIO()->writeError('<info>No local changes</info>');
+            $io->writeError('<info>No local changes</info>');
         } else {
-            $this->getIO()->writeError('<error>You have changes in the following dependencies:</error>');
+            $io->writeError('<error>You have changes in the following dependencies:</error>');
         }
 
         foreach ($errors as $path => $changes) {
@@ -86,15 +91,15 @@ EOT
                 $indentedChanges = implode("\n", array_map(function ($line) {
                     return '    ' . ltrim($line);
                 }, explode("\n", $changes)));
-                $this->getIO()->write('<info>'.$path.'</info>:');
-                $this->getIO()->write($indentedChanges);
+                $io->write('<info>'.$path.'</info>:');
+                $io->write($indentedChanges);
             } else {
-                $this->getIO()->write($path);
+                $io->write($path);
             }
         }
 
         if ($errors && !$input->getOption('verbose')) {
-            $this->getIO()->writeError('Use --verbose (-v) to see modified files');
+            $io->writeError('Use --verbose (-v) to see modified files');
         }
 
         // Dispatch post-status-command

+ 98 - 0
src/Composer/Command/SuggestsCommand.php

@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Command;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SuggestsCommand extends Command
+{
+    protected function configure()
+    {
+        $this
+            ->setName('suggests')
+            ->setDescription('Show package suggestions')
+            ->setDefinition(array(
+                new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
+                new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
+            ))
+            ->setHelp(<<<EOT
+
+The <info>%command.name%</info> command shows suggested packages.
+
+With <info>-v</info> you also see which package suggested it and why.
+
+EOT
+            )
+        ;
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $lock = $this->getComposer()->getLocker()->getLockData();
+
+        if (empty($lock)) {
+            throw new \RuntimeException('Lockfile seems to be empty?');
+        }
+
+        $packages = $lock['packages'];
+
+        if (!$input->getOption('no-dev')) {
+            $packages += $lock['packages-dev'];
+        }
+
+        $filter = $input->getArgument('packages');
+
+        foreach ($packages as $package) {
+            if (empty($package['suggest'])) {
+                continue;
+            }
+
+            if (!empty($filter) && !in_array($package['name'], $filter)) {
+                continue;
+            }
+
+            $this->printSuggestions($packages, $package['name'], $package['suggest']);
+        }
+    }
+
+    protected function printSuggestions($installed, $source, $suggestions)
+    {
+        foreach ($suggestions as $suggestion => $reason) {
+            foreach ($installed as $package) {
+                if ($package['name'] === $suggestion) {
+                    continue 2;
+                }
+            }
+
+            if (empty($reason)) {
+                $reason = '*';
+            }
+
+            $this->printSuggestion($source, $suggestion, $reason);
+        }
+    }
+
+    protected function printSuggestion($package, $suggestion, $reason)
+    {
+        $io = $this->getIO();
+
+        if ($io->isVerbose()) {
+            $io->write(sprintf('<comment>%s</comment> suggests <info>%s</info>: %s', $package, $suggestion, $reason));
+        } else {
+            $io->write(sprintf('<info>%s</info>', $suggestion));
+        }
+    }
+}

+ 7 - 4
src/Composer/Command/UpdateCommand.php

@@ -47,6 +47,7 @@ class UpdateCommand extends Command
                 new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
                 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
                 new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
+                new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
                 new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
                 new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
@@ -74,18 +75,18 @@ EOT
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
+        $io = $this->getIO();
         if ($input->getOption('no-custom-installers')) {
-            $this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
+            $io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
             $input->setOption('no-plugins', true);
         }
 
         if ($input->getOption('dev')) {
-            $this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
+            $io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
         }
 
         $composer = $this->getComposer(true, $input->getOption('no-plugins'));
         $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
-        $io = $this->getIO();
 
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
         $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@@ -114,7 +115,8 @@ EOT
             $preferDist = $input->getOption('prefer-dist');
         }
 
-        $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
+        $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
+        $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
 
         $install
             ->setDryRun($input->getOption('dry-run'))
@@ -125,6 +127,7 @@ EOT
             ->setDumpAutoloader(!$input->getOption('no-autoloader'))
             ->setRunScripts(!$input->getOption('no-scripts'))
             ->setOptimizeAutoloader($optimize)
+            ->setClassMapAuthoritative($authoritative)
             ->setUpdate(true)
             ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
             ->setWhitelistDependencies($input->getOption('with-dependencies'))

+ 77 - 26
src/Composer/Command/ValidateCommand.php

@@ -12,6 +12,7 @@
 
 namespace Composer\Command;
 
+use Composer\Factory;
 use Composer\Package\Loader\ValidatingArrayLoader;
 use Composer\Util\ConfigValidator;
 use Symfony\Component\Console\Input\InputArgument;
@@ -34,14 +35,22 @@ class ValidateCommand extends Command
     {
         $this
             ->setName('validate')
-            ->setDescription('Validates a composer.json')
+            ->setDescription('Validates a composer.json and composer.lock')
             ->setDefinition(array(
                 new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
+                new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'),
                 new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
-                new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
+                new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'),
+                new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'),
+                new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json'),
             ))
             ->setHelp(<<<EOT
-The validate command validates a given composer.json
+The validate command validates a given composer.json and composer.lock
+
+Exit codes in case of errors are:
+1 validation warning(s), only when --strict is given
+2 validation error(s)
+3 file unreadable or missing
 
 EOT
             );
@@ -56,35 +65,86 @@ EOT
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $file = $input->getArgument('file');
+        $io = $this->getIO();
 
         if (!file_exists($file)) {
-            $this->getIO()->writeError('<error>' . $file . ' not found.</error>');
+            $io->writeError('<error>' . $file . ' not found.</error>');
 
-            return 1;
+            return 3;
         }
         if (!is_readable($file)) {
-            $this->getIO()->writeError('<error>' . $file . ' is not readable.</error>');
+            $io->writeError('<error>' . $file . ' is not readable.</error>');
 
-            return 1;
+            return 3;
         }
 
-        $validator = new ConfigValidator($this->getIO());
+        $validator = new ConfigValidator($io);
         $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
         $checkPublish = !$input->getOption('no-check-publish');
+        $checkLock = !$input->getOption('no-check-lock');
+        $isStrict = $input->getOption('strict');
         list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
 
-        // output errors/warnings
+        $lockErrors = array();
+        $composer = Factory::create($io, $file);
+        $locker = $composer->getLocker();
+        if ($locker->isLocked() && !$locker->isFresh()) {
+            $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json.';
+        }
+
+        $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true);
+
+        $exitCode = $errors || ($publishErrors && $checkPublish) || ($lockErrors && $checkLock) ? 2 : ($isStrict && $warnings ? 1 : 0);
+
+        if ($input->getOption('with-dependencies')) {
+            $localRepo = $composer->getRepositoryManager()->getLocalRepository();
+            foreach ($localRepo->getPackages() as $package) {
+                $path = $composer->getInstallationManager()->getInstallPath($package);
+                $file = $path . '/composer.json';
+                if (is_dir($path) && file_exists($file)) {
+                    list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
+                    $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors);
+
+                    $depCode = $errors || ($publishErrors && $checkPublish) ? 2 : ($isStrict && $warnings ? 1 : 0);
+                    $exitCode = max($depCode, $exitCode);
+                }
+            }
+        }
+
+        return $exitCode;
+    }
+
+    private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false)
+    {
         if (!$errors && !$publishErrors && !$warnings) {
-            $this->getIO()->write('<info>' . $file . ' is valid</info>');
+            $io->write('<info>' . $name . ' is valid</info>');
         } elseif (!$errors && !$publishErrors) {
-            $this->getIO()->writeError('<info>' . $file . ' is valid, but with a few warnings</info>');
-            $this->getIO()->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
+            $io->writeError('<info>' . $name . ' is valid, but with a few warnings</info>');
+            if ($printSchemaUrl) {
+                $io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
+            }
         } elseif (!$errors) {
-            $this->getIO()->writeError('<info>' . $file . ' is valid for simple usage with composer but has</info>');
-            $this->getIO()->writeError('<info>strict errors that make it unable to be published as a package:</info>');
-            $this->getIO()->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
+            $io->writeError('<info>' . $name . ' is valid for simple usage with composer but has</info>');
+            $io->writeError('<info>strict errors that make it unable to be published as a package:</info>');
+            if ($printSchemaUrl) {
+                $io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
+            }
         } else {
-            $this->getIO()->writeError('<error>' . $file . ' is invalid, the following errors/warnings were found:</error>');
+            $io->writeError('<error>' . $name . ' is invalid, the following errors/warnings were found:</error>');
+        }
+
+        // If checking publish errors, display them as errors, otherwise just show them as warnings
+        if ($checkPublish) {
+            $errors = array_merge($errors, $publishErrors);
+        } else {
+            $warnings = array_merge($warnings, $publishErrors);
+        }
+
+        // If checking lock errors, display them as errors, otherwise just show them as warnings
+        if ($checkLock) {
+            $errors = array_merge($errors, $lockErrors);
+        } else {
+            $warnings = array_merge($warnings, $lockErrors);
         }
 
         $messages = array(
@@ -92,19 +152,10 @@ EOT
             'warning' => $warnings,
         );
 
-        // If checking publish errors, display them errors, otherwise just show them as warnings
-        if ($checkPublish) {
-            $messages['error'] = array_merge($messages['error'], $publishErrors);
-        } else {
-            $messages['warning'] = array_merge($messages['warning'], $publishErrors);
-        }
-
         foreach ($messages as $style => $msgs) {
             foreach ($msgs as $msg) {
-                $this->getIO()->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
+                $io->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
             }
         }
-
-        return $errors || ($publishErrors && $checkPublish) ? 1 : 0;
     }
 }

+ 6 - 2
src/Composer/Compiler.php

@@ -13,6 +13,7 @@
 namespace Composer;
 
 use Composer\Json\JsonFile;
+use Composer\Spdx\SpdxLicenses;
 use Symfony\Component\Finder\Finder;
 use Symfony\Component\Process\Process;
 use Seld\PharUtils\Timestamps;
@@ -32,8 +33,8 @@ class Compiler
     /**
      * Compiles composer into a single phar file
      *
-     * @throws \RuntimeException
      * @param  string            $pharFile The full path to the file to create
+     * @throws \RuntimeException
      */
     public function compile($pharFile = 'composer.phar')
     {
@@ -95,7 +96,8 @@ class Compiler
         $finder = new Finder();
         $finder->files()
             ->name('*.json')
-            ->in(__DIR__ . '/../../res')
+            ->in(__DIR__.'/../../res')
+            ->in(SpdxLicenses::getResourcesDir())
             ->sort($finderSort)
         ;
 
@@ -116,6 +118,8 @@ class Compiler
             ->in(__DIR__.'/../../vendor/seld/jsonlint/')
             ->in(__DIR__.'/../../vendor/seld/cli-prompt/')
             ->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
+            ->in(__DIR__.'/../../vendor/composer/spdx-licenses/')
+            ->in(__DIR__.'/../../vendor/composer/semver/')
             ->sort($finderSort)
         ;
 

+ 5 - 6
src/Composer/Config.php

@@ -13,7 +13,6 @@
 namespace Composer;
 
 use Composer\Config\ConfigSourceInterface;
-use Composer\Plugin\PluginInterface;
 
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
@@ -59,7 +58,7 @@ class Config
             'type' => 'composer',
             'url' => 'https?://packagist.org',
             'allow_ssl_downgrade' => true,
-        )
+        ),
     );
 
     private $config;
@@ -70,7 +69,7 @@ class Config
     private $useEnvironment;
 
     /**
-     * @param boolean $useEnvironment Use COMPOSER_ environment variables to replace config settings
+     * @param bool $useEnvironment Use COMPOSER_ environment variables to replace config settings
      */
     public function __construct($useEnvironment = true, $baseDir = null)
     {
@@ -183,7 +182,7 @@ class Config
                     return $val;
                 }
 
-                return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val);
+                return ($flags & self::RELATIVE_PATHS == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
 
             case 'cache-ttl':
                 return (int) $this->config[$key];
@@ -334,8 +333,8 @@ class Config
      * This should be used to read COMPOSER_ environment variables
      * that overload config values.
      *
-     * @param  string         $var
-     * @return string|boolean
+     * @param  string      $var
+     * @return string|bool
      */
     private function getComposerEnv($var)
     {

+ 17 - 13
src/Composer/Console/Application.php

@@ -89,9 +89,10 @@ class Application extends BaseApplication
     {
         $this->io = new ConsoleIO($input, $output, $this->getHelperSet());
         ErrorHandler::register($this->io);
+        $io = $this->getIO();
 
         if (PHP_VERSION_ID < 50302) {
-            $this->getIO()->writeError('<warning>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.</warning>');
+            $io->writeError('<warning>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.</warning>');
         }
 
         if (defined('COMPOSER_DEV_WARNING_TIME')) {
@@ -104,7 +105,7 @@ class Application extends BaseApplication
             }
             if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
                 if (time() > COMPOSER_DEV_WARNING_TIME) {
-                    $this->getIO()->writeError(sprintf('<warning>Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
+                    $io->writeError(sprintf('<warning>Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
                 }
             }
         }
@@ -117,8 +118,8 @@ class Application extends BaseApplication
         if ($newWorkDir = $this->getNewWorkingDir($input)) {
             $oldWorkingDir = getcwd();
             chdir($newWorkDir);
-            if ($this->getIO()->isDebug() >= 4) {
-                $this->getIO()->writeError('Changed CWD to ' . getcwd());
+            if ($io->isDebug() >= 4) {
+                $io->writeError('Changed CWD to ' . getcwd());
             }
         }
 
@@ -129,7 +130,7 @@ class Application extends BaseApplication
                 foreach ($composer['scripts'] as $script => $dummy) {
                     if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
                         if ($this->has($script)) {
-                            $this->getIO()->writeError('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
+                            $io->writeError('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
                         } else {
                             $this->add(new Command\ScriptAliasCommand($script));
                         }
@@ -150,7 +151,7 @@ class Application extends BaseApplication
         }
 
         if (isset($startTime)) {
-            $this->getIO()->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
+            $io->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
         }
 
         return $result;
@@ -158,8 +159,8 @@ class Application extends BaseApplication
 
     /**
      * @param  InputInterface    $input
-     * @return string
      * @throws \RuntimeException
+     * @return string
      */
     private function getNewWorkingDir(InputInterface $input)
     {
@@ -176,30 +177,32 @@ class Application extends BaseApplication
      */
     public function renderException($exception, $output)
     {
+        $io = $this->getIO();
+
         try {
             $composer = $this->getComposer(false, true);
             if ($composer) {
                 $config = $composer->getConfig();
 
-                $minSpaceFree = 1024*1024;
+                $minSpaceFree = 1024 * 1024;
                 if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
                     || (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
                     || (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
                 ) {
-                    $this->getIO()->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
+                    $io->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
                 }
             }
         } catch (\Exception $e) {
         }
 
         if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
-            $this->getIO()->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
-            $this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
+            $io->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
+            $io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
         }
 
         if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
-            $this->getIO()->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
-            $this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
+            $io->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
+            $io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
         }
 
         if ($output instanceof ConsoleOutputInterface) {
@@ -272,6 +275,7 @@ class Application extends BaseApplication
         $commands[] = new Command\SearchCommand();
         $commands[] = new Command\ValidateCommand();
         $commands[] = new Command\ShowCommand();
+        $commands[] = new Command\SuggestsCommand();
         $commands[] = new Command\RequireCommand();
         $commands[] = new Command\DumpAutoloadCommand();
         $commands[] = new Command\StatusCommand();

+ 3 - 2
src/Composer/Console/HtmlOutputFormatter.php

@@ -27,7 +27,7 @@ class HtmlOutputFormatter extends OutputFormatter
         34 => 'blue',
         35 => 'magenta',
         36 => 'cyan',
-        37 => 'white'
+        37 => 'white',
     );
     private static $availableBackgroundColors = array(
         40 => 'black',
@@ -37,7 +37,7 @@ class HtmlOutputFormatter extends OutputFormatter
         44 => 'blue',
         45 => 'magenta',
         46 => 'cyan',
-        47 => 'white'
+        47 => 'white',
     );
     private static $availableOptions = array(
         1 => 'bold',
@@ -60,6 +60,7 @@ class HtmlOutputFormatter extends OutputFormatter
         $formatted = parent::format($message);
 
         $clearEscapeCodes = '(?:39|49|0|22|24|25|27|28)';
+
         return preg_replace_callback("{\033\[([0-9;]+)m(.*?)\033\[(?:".$clearEscapeCodes.";)*?".$clearEscapeCodes."m}s", array($this, 'formatHtml'), $formatted);
     }
 

+ 4 - 4
src/Composer/DependencyResolver/DefaultPolicy.php

@@ -15,7 +15,7 @@ namespace Composer\DependencyResolver;
 use Composer\Package\PackageInterface;
 use Composer\Package\AliasPackage;
 use Composer\Package\BasePackage;
-use Composer\Package\LinkConstraint\VersionConstraint;
+use Composer\Semver\Constraint\Constraint;
 
 /**
  * @author Nils Adermann <naderman@naderman.de>
@@ -38,8 +38,8 @@ class DefaultPolicy implements PolicyInterface
             return BasePackage::$stabilities[$stabA] < BasePackage::$stabilities[$stabB];
         }
 
-        $constraint = new VersionConstraint($operator, $b->getVersion());
-        $version = new VersionConstraint('==', $a->getVersion());
+        $constraint = new Constraint($operator, $b->getVersion());
+        $version = new Constraint('==', $a->getVersion());
 
         return $constraint->matchSpecific($version, true);
     }
@@ -194,7 +194,7 @@ class DefaultPolicy implements PolicyInterface
         foreach ($source->getReplaces() as $link) {
             if ($link->getTarget() === $target->getName()
 //                && (null === $link->getConstraint() ||
-//                $link->getConstraint()->matches(new VersionConstraint('==', $target->getVersion())))) {
+//                $link->getConstraint()->matches(new Constraint('==', $target->getVersion())))) {
                 ) {
                 return true;
             }

+ 1 - 0
src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php

@@ -13,6 +13,7 @@
 namespace Composer\DependencyResolver\Operation;
 
 use Composer\Package\AliasPackage;
+use Composer\Package\PackageInterface;
 
 /**
  * Solver install operation.

+ 1 - 0
src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php

@@ -13,6 +13,7 @@
 namespace Composer\DependencyResolver\Operation;
 
 use Composer\Package\AliasPackage;
+use Composer\Package\PackageInterface;
 
 /**
  * Solver install operation.

+ 1 - 2
src/Composer/DependencyResolver/Operation/SolverOperation.php

@@ -12,7 +12,6 @@
 
 namespace Composer\DependencyResolver\Operation;
 
-use Composer\Package\Version\VersionParser;
 use Composer\Package\PackageInterface;
 
 /**
@@ -46,6 +45,6 @@ abstract class SolverOperation implements OperationInterface
 
     protected function formatVersion(PackageInterface $package)
     {
-        return VersionParser::formatVersion($package);
+        return $package->getFullPrettyVersion();
     }
 }

+ 31 - 23
src/Composer/DependencyResolver/Pool.php

@@ -14,10 +14,10 @@ namespace Composer\DependencyResolver;
 
 use Composer\Package\BasePackage;
 use Composer\Package\AliasPackage;
-use Composer\Package\Version\VersionParser;
-use Composer\Package\LinkConstraint\LinkConstraintInterface;
-use Composer\Package\LinkConstraint\VersionConstraint;
-use Composer\Package\LinkConstraint\EmptyConstraint;
+use Composer\Semver\VersionParser;
+use Composer\Semver\Constraint\ConstraintInterface;
+use Composer\Semver\Constraint\Constraint;
+use Composer\Semver\Constraint\EmptyConstraint;
 use Composer\Repository\RepositoryInterface;
 use Composer\Repository\CompositeRepository;
 use Composer\Repository\ComposerRepository;
@@ -31,7 +31,7 @@ use Composer\Package\PackageInterface;
  * @author Nils Adermann <naderman@naderman.de>
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
-class Pool
+class Pool implements \Countable
 {
     const MATCH_NAME = -1;
     const MATCH_NONE = 0;
@@ -150,27 +150,35 @@ class Pool
     }
 
     /**
-    * Retrieves the package object for a given package id.
-    *
-    * @param int $id
-    * @return PackageInterface
-    */
+     * Retrieves the package object for a given package id.
+     *
+     * @param  int              $id
+     * @return PackageInterface
+     */
     public function packageById($id)
     {
         return $this->packages[$id - 1];
     }
 
+    /**
+     * Returns how many packages have been loaded into the pool
+     */
+    public function count()
+    {
+        return count($this->packages);
+    }
+
     /**
      * Searches all packages providing the given package name and match the constraint
      *
-     * @param  string                  $name          The package name to be searched for
-     * @param  LinkConstraintInterface $constraint    A constraint that all returned
-     *                                                packages must match or null to return all
-     * @param  bool                    $mustMatchName Whether the name of returned packages
-     *                                                must match the given name
-     * @return PackageInterface[]      A set of packages
+     * @param  string              $name          The package name to be searched for
+     * @param  ConstraintInterface $constraint    A constraint that all returned
+     *                                            packages must match or null to return all
+     * @param  bool                $mustMatchName Whether the name of returned packages
+     *                                            must match the given name
+     * @return PackageInterface[]  A set of packages
      */
-    public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false)
+    public function whatProvides($name, ConstraintInterface $constraint = null, $mustMatchName = false)
     {
         $key = ((int) $mustMatchName).$constraint;
         if (isset($this->providerCache[$name][$key])) {
@@ -309,12 +317,12 @@ class Pool
      * Checks if the package matches the given constraint directly or through
      * provided or replaced packages
      *
-     * @param  array|PackageInterface  $candidate
-     * @param  string                  $name       Name of the package to be matched
-     * @param  LinkConstraintInterface $constraint The constraint to verify
-     * @return int                     One of the MATCH* constants of this class or 0 if there is no match
+     * @param  array|PackageInterface $candidate
+     * @param  string                 $name       Name of the package to be matched
+     * @param  ConstraintInterface    $constraint The constraint to verify
+     * @return int                    One of the MATCH* constants of this class or 0 if there is no match
      */
-    private function match($candidate, $name, LinkConstraintInterface $constraint = null)
+    private function match($candidate, $name, ConstraintInterface $constraint = null)
     {
         $candidateName = $candidate->getName();
         $candidateVersion = $candidate->getVersion();
@@ -328,7 +336,7 @@ class Pool
         }
 
         if ($candidateName === $name) {
-            $pkgConstraint = new VersionConstraint('==', $candidateVersion);
+            $pkgConstraint = new Constraint('==', $candidateVersion);
 
             if ($constraint === null || $constraint->matches($pkgConstraint)) {
                 return $requireFilter->matches($pkgConstraint) ? self::MATCH : self::MATCH_FILTERED;

+ 7 - 3
src/Composer/DependencyResolver/Problem.php

@@ -47,7 +47,7 @@ class Problem
      */
     public function addRule(Rule $rule)
     {
-        $this->addReason($rule->getId(), array(
+        $this->addReason(spl_object_hash($rule), array(
             'rule' => $rule,
             'job' => $rule->getJob(),
         ));
@@ -87,8 +87,12 @@ class Problem
             }
 
             if ($job && $job['cmd'] === 'install' && empty($packages)) {
+
                 // handle php/hhvm
                 if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
+                    $available = $this->pool->whatProvides($job['packageName']);
+                    $version = count($available) ? $available[0]->getPrettyVersion() : phpversion();
+
                     $msg = "\n    - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
 
                     if (defined('HHVM_VERSION')) {
@@ -97,7 +101,7 @@ class Problem
                         return $msg . 'you are running this with PHP and not HHVM.';
                     }
 
-                    return $msg . 'your PHP version ('.  phpversion().') does not satisfy that requirement.';
+                    return $msg . 'your PHP version ('. $version .') does not satisfy that requirement.';
                 }
 
                 // handle php extensions
@@ -218,7 +222,7 @@ class Problem
     /**
      * Turns a constraint into text usable in a sentence describing a job
      *
-     * @param  \Composer\Package\LinkConstraint\LinkConstraintInterface $constraint
+     * @param  \Composer\Semver\Constraint\ConstraintInterface $constraint
      * @return string
      */
     protected function constraintToText($constraint)

+ 7 - 7
src/Composer/DependencyResolver/Request.php

@@ -12,7 +12,7 @@
 
 namespace Composer\DependencyResolver;
 
-use Composer\Package\LinkConstraint\LinkConstraintInterface;
+use Composer\Semver\Constraint\ConstraintInterface;
 
 /**
  * @author Nils Adermann <naderman@naderman.de>
@@ -26,17 +26,17 @@ class Request
         $this->jobs = array();
     }
 
-    public function install($packageName, LinkConstraintInterface $constraint = null)
+    public function install($packageName, ConstraintInterface $constraint = null)
     {
         $this->addJob($packageName, 'install', $constraint);
     }
 
-    public function update($packageName, LinkConstraintInterface $constraint = null)
+    public function update($packageName, ConstraintInterface $constraint = null)
     {
         $this->addJob($packageName, 'update', $constraint);
     }
 
-    public function remove($packageName, LinkConstraintInterface $constraint = null)
+    public function remove($packageName, ConstraintInterface $constraint = null)
     {
         $this->addJob($packageName, 'remove', $constraint);
     }
@@ -46,12 +46,12 @@ class Request
      *
      * These jobs will not be tempered with by the solver
      */
-    public function fix($packageName, LinkConstraintInterface $constraint = null)
+    public function fix($packageName, ConstraintInterface $constraint = null)
     {
         $this->addJob($packageName, 'install', $constraint, true);
     }
 
-    protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null, $fixed = false)
+    protected function addJob($packageName, $cmd, ConstraintInterface $constraint = null, $fixed = false)
     {
         $packageName = strtolower($packageName);
 
@@ -59,7 +59,7 @@ class Request
             'cmd' => $cmd,
             'packageName' => $packageName,
             'constraint' => $constraint,
-            'fixed' => $fixed
+            'fixed' => $fixed,
         );
     }
 

+ 27 - 42
src/Composer/DependencyResolver/Rule.php

@@ -29,63 +29,51 @@ class Rule
     const RULE_LEARNED = 12;
     const RULE_PACKAGE_ALIAS = 13;
 
+    const BITFIELD_TYPE = 0;
+    const BITFIELD_REASON = 8;
+    const BITFIELD_DISABLED = 16;
+
     /**
      * READ-ONLY: The literals this rule consists of.
      * @var array
      */
     public $literals;
 
-    protected $disabled;
-    protected $type;
-    protected $id;
-    protected $reason;
+    protected $bitfield;
     protected $reasonData;
 
-    protected $job;
-
-    protected $ruleHash;
-
     public function __construct(array $literals, $reason, $reasonData, $job = null)
     {
         // sort all packages ascending by id
         sort($literals);
 
         $this->literals = $literals;
-        $this->reason = $reason;
         $this->reasonData = $reasonData;
 
-        $this->disabled = false;
-
-        $this->job = $job;
-
-        $this->type = -1;
+        if ($job) {
+            $this->job = $job;
+        }
 
-        $this->ruleHash = substr(md5(implode(',', $this->literals)), 0, 5);
+        $this->bitfield = (0 << self::BITFIELD_DISABLED) |
+            ($reason << self::BITFIELD_REASON) |
+            (255 << self::BITFIELD_TYPE);
     }
 
     public function getHash()
     {
-        return $this->ruleHash;
-    }
-
-    public function setId($id)
-    {
-        $this->id = $id;
-    }
+        $data = unpack('ihash', md5(implode(',', $this->literals), true));
 
-    public function getId()
-    {
-        return $this->id;
+        return $data['hash'];
     }
 
     public function getJob()
     {
-        return $this->job;
+        return isset($this->job) ? $this->job : null;
     }
 
     public function getReason()
     {
-        return $this->reason;
+        return ($this->bitfield & (255 << self::BITFIELD_REASON)) >> self::BITFIELD_REASON;
     }
 
     public function getReasonData()
@@ -95,11 +83,11 @@ class Rule
 
     public function getRequiredPackage()
     {
-        if ($this->reason === self::RULE_JOB_INSTALL) {
+        if ($this->getReason() === self::RULE_JOB_INSTALL) {
             return $this->reasonData;
         }
 
-        if ($this->reason === self::RULE_PACKAGE_REQUIRES) {
+        if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
             return $this->reasonData->getTarget();
         }
     }
@@ -114,10 +102,6 @@ class Rule
      */
     public function equals(Rule $rule)
     {
-        if ($this->ruleHash !== $rule->ruleHash) {
-            return false;
-        }
-
         if (count($this->literals) != count($rule->literals)) {
             return false;
         }
@@ -133,32 +117,32 @@ class Rule
 
     public function setType($type)
     {
-        $this->type = $type;
+        $this->bitfield = ($this->bitfield & ~(255 << self::BITFIELD_TYPE)) | ((255 & $type) << self::BITFIELD_TYPE);
     }
 
     public function getType()
     {
-        return $this->type;
+        return ($this->bitfield & (255 << self::BITFIELD_TYPE)) >> self::BITFIELD_TYPE;
     }
 
     public function disable()
     {
-        $this->disabled = true;
+        $this->bitfield = ($this->bitfield & ~(255 << self::BITFIELD_DISABLED)) | (1 << self::BITFIELD_DISABLED);
     }
 
     public function enable()
     {
-        $this->disabled = false;
+        $this->bitfield = $this->bitfield & ~(255 << self::BITFIELD_DISABLED);
     }
 
     public function isDisabled()
     {
-        return $this->disabled;
+        return (bool) (($this->bitfield & (255 << self::BITFIELD_DISABLED)) >> self::BITFIELD_DISABLED);
     }
 
     public function isEnabled()
     {
-        return !$this->disabled;
+        return !(($this->bitfield & (255 << self::BITFIELD_DISABLED)) >> self::BITFIELD_DISABLED);
     }
 
     /**
@@ -184,7 +168,7 @@ class Rule
             $ruleText .= $pool->literalToPrettyString($literal, $installedMap);
         }
 
-        switch ($this->reason) {
+        switch ($this->getReason()) {
             case self::RULE_INTERNAL_ALLOW_UPDATE:
                 return $ruleText;
 
@@ -216,16 +200,17 @@ class Rule
                 } else {
                     $targetName = $this->reasonData->getTarget();
 
-                    // handle php extensions
                     if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
+                        // handle php/hhvm
                         if (defined('HHVM_VERSION')) {
                             $text .= ' -> your HHVM version does not satisfy that requirement.';
                         } elseif ($targetName === 'hhvm') {
                             $text .= ' -> you are running this with PHP and not HHVM.';
                         } else {
-                            $text .= ' -> your PHP version ('.  phpversion().') does not satisfy that requirement.';
+                            $text .= ' -> your PHP version ('. phpversion() .') or "config.platform.php" value does not satisfy that requirement.';
                         }
                     } elseif (0 === strpos($targetName, 'ext-')) {
+                        // handle php extensions
                         $ext = substr($targetName, 4);
                         $error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
 

+ 2 - 3
src/Composer/DependencyResolver/RuleSet.php

@@ -30,7 +30,7 @@ class RuleSet implements \IteratorAggregate, \Countable
     public $ruleById;
 
     protected static $types = array(
-        -1 => 'UNKNOWN',
+        255 => 'UNKNOWN',
         self::TYPE_PACKAGE => 'PACKAGE',
         self::TYPE_JOB => 'JOB',
         self::TYPE_LEARNED => 'LEARNED',
@@ -66,7 +66,6 @@ class RuleSet implements \IteratorAggregate, \Countable
         $this->ruleById[$this->nextRuleId] = $rule;
         $rule->setType($type);
 
-        $rule->setId($this->nextRuleId);
         $this->nextRuleId++;
 
         $hash = $rule->getHash();
@@ -131,7 +130,7 @@ class RuleSet implements \IteratorAggregate, \Countable
     public function getTypes()
     {
         $types = self::$types;
-        unset($types[-1]);
+        unset($types[255]);
 
         return array_keys($types);
     }

+ 1 - 1
src/Composer/DependencyResolver/RuleWatchNode.php

@@ -83,7 +83,7 @@ class RuleWatchNode
     /**
      * Given one watched literal, this method returns the other watched literal
      *
-     * @param int The watched literal that should not be returned
+     * @param  int $literal The watched literal that should not be returned
      * @return int A literal
      */
     public function getOtherWatch($literal)

+ 9 - 4
src/Composer/DependencyResolver/Solver.php

@@ -50,6 +50,11 @@ class Solver
         $this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
     }
 
+    public function getRuleSetSize()
+    {
+        return count($this->rules);
+    }
+
     // aka solver_makeruledecisions
     private function makeAssertionRuleDecisions()
     {
@@ -212,7 +217,7 @@ class Solver
      * Evaluates each term affected by the decision (linked through watches)
      * If we find unit rules we make new decisions based on them
      *
-     * @param  integer   $level
+     * @param  int       $level
      * @return Rule|null A rule on conflict, otherwise null.
      */
     protected function propagate($level)
@@ -314,7 +319,7 @@ class Solver
 
             $this->rules->add($newRule, RuleSet::TYPE_LEARNED);
 
-            $this->learnedWhy[$newRule->getId()] = $why;
+            $this->learnedWhy[spl_object_hash($newRule)] = $why;
 
             $ruleNode = new RuleWatchNode($newRule);
             $ruleNode->watch2OnHighest($this->decisions);
@@ -449,7 +454,7 @@ class Solver
 
     private function analyzeUnsolvableRule($problem, $conflictRule)
     {
-        $why = $conflictRule->getId();
+        $why = spl_object_hash($conflictRule);
 
         if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
             $learnedWhy = $this->learnedWhy[$why];
@@ -567,7 +572,7 @@ class Solver
     private function enableDisableLearnedRules()
     {
         foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
-            $why = $this->learnedWhy[$rule->getId()];
+            $why = $this->learnedWhy[spl_object_hash($rule)];
             $problemRules = $this->learnedPool[$why];
 
             $foundDisabled = false;

+ 1 - 1
src/Composer/DependencyResolver/SolverProblemsException.php

@@ -32,7 +32,7 @@ class SolverProblemsException extends \RuntimeException
     {
         $text = "\n";
         foreach ($this->problems as $i => $problem) {
-            $text .= "  Problem ".($i+1).$problem->getPrettyString($this->installedMap)."\n";
+            $text .= "  Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n";
         }
 
         if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) {

+ 6 - 2
src/Composer/Downloader/ArchiveDownloader.php

@@ -77,7 +77,11 @@ abstract class ArchiveDownloader extends FileDownloader
 
                 // retry downloading if we have an invalid zip file
                 if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
-                    $this->io->writeError('    Invalid zip file, retrying...');
+                    if ($this->io->isDebug()) {
+                        $this->io->writeError('    Invalid zip file ('.$e->getMessage().'), retrying...');
+                    } else {
+                        $this->io->writeError('    Invalid zip file, retrying...');
+                    }
                     usleep(500000);
                     continue;
                 }
@@ -115,7 +119,7 @@ abstract class ArchiveDownloader extends FileDownloader
                 // update api archives to the proper reference
                 $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
             }
-        } else if ($package->getDistReference() && strpos($url, 'bitbucket.org')) {
+        } elseif ($package->getDistReference() && strpos($url, 'bitbucket.org')) {
             if (preg_match('{^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$}i', $url, $match)) {
                 // update Bitbucket archives to the proper reference
                 $url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $package->getDistReference() . '.' . $match[4];

+ 5 - 7
src/Composer/Downloader/DownloadManager.php

@@ -103,10 +103,9 @@ class DownloadManager
     /**
      * Returns downloader for a specific installation type.
      *
-     * @param  string              $type installation type
-     * @return DownloaderInterface
-     *
+     * @param  string                    $type installation type
      * @throws \InvalidArgumentException if downloader for provided type is not registered
+     * @return DownloaderInterface
      */
     public function getDownloader($type)
     {
@@ -121,12 +120,11 @@ class DownloadManager
     /**
      * Returns downloader for already installed package.
      *
-     * @param  PackageInterface         $package package instance
-     * @return DownloaderInterface|null
-     *
+     * @param  PackageInterface          $package package instance
      * @throws \InvalidArgumentException if package has no installation source specified
      * @throws \LogicException           if specific downloader used to load package with
-     *                                   wrong type
+     *                                           wrong type
+     * @return DownloaderInterface|null
      */
     public function getDownloaderForInstalledPackage(PackageInterface $package)
     {

+ 6 - 8
src/Composer/Downloader/FileDownloader.php

@@ -16,7 +16,6 @@ use Composer\Config;
 use Composer\Cache;
 use Composer\IO\IOInterface;
 use Composer\Package\PackageInterface;
-use Composer\Package\Version\VersionParser;
 use Composer\Plugin\PluginEvents;
 use Composer\Plugin\PreFileDownloadEvent;
 use Composer\EventDispatcher\EventDispatcher;
@@ -81,7 +80,7 @@ class FileDownloader implements DownloaderInterface
             throw new \InvalidArgumentException('The given package is missing url information');
         }
 
-        $this->io->writeError("  - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
+        $this->io->writeError("  - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
 
         $urls = $package->getDistUrls();
         while ($url = array_shift($urls)) {
@@ -138,7 +137,7 @@ class FileDownloader implements DownloaderInterface
                         break;
                     } catch (TransportException $e) {
                         // if we got an http response with a proper code, then requesting again will probably not help, abort
-                        if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
+                        if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) {
                             throw $e;
                         }
                         if ($this->io->isVerbose()) {
@@ -205,7 +204,7 @@ class FileDownloader implements DownloaderInterface
      */
     public function remove(PackageInterface $package, $path)
     {
-        $this->io->writeError("  - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
+        $this->io->writeError("  - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
         if (!$this->filesystem->removeDirectory($path)) {
             throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
         }
@@ -226,11 +225,10 @@ class FileDownloader implements DownloaderInterface
     /**
      * Process the download url
      *
-     * @param  PackageInterface $package package the url is coming from
-     * @param  string           $url     download url
-     * @return string           url
-     *
+     * @param  PackageInterface  $package package the url is coming from
+     * @param  string            $url     download url
      * @throws \RuntimeException If any problem with the url
+     * @return string            url
      */
     protected function processUrl(PackageInterface $package, $url)
     {

+ 26 - 9
src/Composer/Downloader/GitDownloader.php

@@ -13,7 +13,6 @@
 namespace Composer\Downloader;
 
 use Composer\Package\PackageInterface;
-use Composer\Util\GitHub;
 use Composer\Util\Git as GitUtil;
 use Composer\Util\ProcessExecutor;
 use Composer\IO\IOInterface;
@@ -82,7 +81,7 @@ class GitDownloader extends VcsDownloader
         $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
 
         $commandCallable = function ($url) use ($command) {
-            return sprintf($command, ProcessExecutor::escape ($url));
+            return sprintf($command, ProcessExecutor::escape($url));
         };
 
         $this->gitUtil->runCommand($commandCallable, $url, $path);
@@ -150,7 +149,7 @@ class GitDownloader extends VcsDownloader
         }
 
         while (true) {
-            switch ($this->io->ask('    <info>Discard changes [y,n,v,'.($update ? 's,' : '').'?]?</info> ', '?')) {
+            switch ($this->io->ask('    <info>Discard changes [y,n,v,d,'.($update ? 's,' : '').'?]?</info> ', '?')) {
                 case 'y':
                     $this->discardChanges($path);
                     break 2;
@@ -170,6 +169,10 @@ class GitDownloader extends VcsDownloader
                     $this->io->writeError($changes);
                     break;
 
+                case 'd':
+                    $this->viewDiff($path);
+                    break;
+
                 case '?':
                 default:
                     help:
@@ -177,6 +180,7 @@ class GitDownloader extends VcsDownloader
                         '    y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
                         '    n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
                         '    v - view modified files',
+                        '    d - view local modifications (diff)',
                     ));
                     if ($update) {
                         $this->io->writeError('    s - stash changes and try to reapply them after the update');
@@ -205,13 +209,12 @@ class GitDownloader extends VcsDownloader
     /**
      * Updates the given path to the given commit ref
      *
-     * @param  string      $path
-     * @param  string      $reference
-     * @param  string      $branch
-     * @param  \DateTime   $date
-     * @return null|string if a string is returned, it is the commit reference that was checked out if the original could not be found
-     *
+     * @param  string            $path
+     * @param  string            $reference
+     * @param  string            $branch
+     * @param  \DateTime         $date
      * @throws \RuntimeException
+     * @return null|string       if a string is returned, it is the commit reference that was checked out if the original could not be found
      */
     protected function updateToCommit($path, $reference, $branch, $date)
     {
@@ -327,6 +330,20 @@ class GitDownloader extends VcsDownloader
         $this->hasStashedChanges = true;
     }
 
+    /**
+     * @param $path
+     * @throws \RuntimeException
+     */
+    protected function viewDiff($path)
+    {
+        $path = $this->normalizePath($path);
+        if (0 !== $this->process->execute('git diff HEAD', $output, $path)) {
+            throw new \RuntimeException("Could not view diff\n\n:".$this->process->getErrorOutput());
+        }
+
+        $this->io->writeError($output);
+    }
+
     protected function normalizePath($path)
     {
         if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) {

+ 61 - 0
src/Composer/Downloader/PathDownloader.php

@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Downloader;
+
+use Composer\Package\PackageInterface;
+use Symfony\Component\Filesystem\Exception\IOException;
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * Download a package from a local path.
+ *
+ * @author Samuel Roze <samuel.roze@gmail.com>
+ * @author Johann Reinke <johann.reinke@gmail.com>
+ */
+class PathDownloader extends FileDownloader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function download(PackageInterface $package, $path)
+    {
+        $fileSystem = new Filesystem();
+        $this->filesystem->removeDirectory($path);
+
+        $this->io->writeError(sprintf(
+            '  - Installing <info>%s</info> (<comment>%s</comment>)',
+            $package->getName(),
+            $package->getFullPrettyVersion()
+        ));
+
+        $url = $package->getDistUrl();
+        $realUrl = realpath($url);
+        if (false === $realUrl || !file_exists($realUrl) || !is_dir($realUrl)) {
+            throw new \RuntimeException(sprintf(
+                'Path "%s" is not found',
+                $url
+            ));
+        }
+
+        try {
+            $shortestPath = $this->filesystem->findShortestPath($path, $realUrl);
+            $fileSystem->symlink($shortestPath, $path);
+            $this->io->writeError(sprintf('    Symlinked from %s', $url));
+        } catch (IOException $e) {
+            $fileSystem->mirror($realUrl, $path);
+            $this->io->writeError(sprintf('    Mirrored from %s', $url));
+        }
+
+        $this->io->writeError('');
+    }
+}

+ 1 - 2
src/Composer/Downloader/PearPackageExtractor.php

@@ -48,7 +48,6 @@ class PearPackageExtractor
      * @param  array                     $vars   used for replacement tasks
      * @throws \RuntimeException
      * @throws \UnexpectedValueException
-     *
      */
     public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
     {
@@ -130,9 +129,9 @@ class PearPackageExtractor
      * @param  string            $source string path to extracted files
      * @param  array             $roles  array [role => roleRoot] relative root for files having that role
      * @param  array             $vars   list of values can be used for replacement tasks
+     * @throws \RuntimeException
      * @return array             array of 'source' => 'target', where source is location of file in the tarball (relative to source
      *                                  path, and target is destination of file (also relative to $source path)
-     * @throws \RuntimeException
      */
     private function buildCopyActions($source, array $roles, $vars)
     {

+ 1 - 1
src/Composer/Downloader/PerforceDownloader.php

@@ -43,7 +43,7 @@ class PerforceDownloader extends VcsDownloader
 
     private function getLabelFromSourceReference($ref)
     {
-        $pos = strpos($ref,'@');
+        $pos = strpos($ref, '@');
         if (false !== $pos) {
             return substr($ref, $pos + 1);
         }

+ 1 - 1
src/Composer/Downloader/SvnDownloader.php

@@ -151,7 +151,7 @@ class SvnDownloader extends VcsDownloader
      */
     protected function getCommitLogs($fromReference, $toReference, $path)
     {
-        if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference) ) {
+        if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference)) {
             // strip paths from references and only keep the actual revision
             $fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference);
             $toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference);

+ 3 - 4
src/Composer/Downloader/VcsDownloader.php

@@ -14,7 +14,6 @@ namespace Composer\Downloader;
 
 use Composer\Config;
 use Composer\Package\PackageInterface;
-use Composer\Package\Version\VersionParser;
 use Composer\Util\ProcessExecutor;
 use Composer\IO\IOInterface;
 use Composer\Util\Filesystem;
@@ -54,7 +53,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
             throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
         }
 
-        $this->io->writeError("  - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
+        $this->io->writeError("  - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
         $this->filesystem->emptyDirectory($path);
 
         $urls = $package->getSourceUrls();
@@ -100,8 +99,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
             }
             $name .= ' '.$initial->getPrettyVersion();
         } else {
-            $from = VersionParser::formatVersion($initial);
-            $to = VersionParser::formatVersion($target);
+            $from = $initial->getFullPrettyVersion();
+            $to = $target->getFullPrettyVersion();
         }
 
         $this->io->writeError("  - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");

+ 2 - 2
src/Composer/EventDispatcher/Event.php

@@ -35,7 +35,7 @@ class Event
     protected $flags;
 
     /**
-     * @var boolean Whether the event should not be passed to more listeners
+     * @var bool Whether the event should not be passed to more listeners
      */
     private $propagationStopped = false;
 
@@ -86,7 +86,7 @@ class Event
     /**
      * Checks if stopPropagation has been called
      *
-     * @return boolean Whether propagation has been stopped
+     * @return bool Whether propagation has been stopped
      */
     public function isPropagationStopped()
     {

+ 24 - 11
src/Composer/EventDispatcher/EventDispatcher.php

@@ -21,6 +21,7 @@ use Composer\Composer;
 use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\Repository\CompositeRepository;
 use Composer\Script;
+use Composer\Script\CommandEvent;
 use Composer\Script\PackageEvent;
 use Composer\Util\ProcessExecutor;
 
@@ -135,10 +136,10 @@ class EventDispatcher
      *
      * @param  Event             $event          The event object to pass to the event handlers/listeners.
      * @param  string            $additionalArgs
-     * @return int               return code of the executed script if any, for php scripts a false return
-     *                                          value is changed to 1, anything else to 0
      * @throws \RuntimeException
      * @throws \Exception
+     * @return int               return code of the executed script if any, for php scripts a false return
+     *                                          value is changed to 1, anything else to 0
      */
     protected function doDispatch(Event $event)
     {
@@ -170,8 +171,14 @@ class EventDispatcher
                     throw $e;
                 }
             } else {
-                $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments()));
-                if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) {
+                $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments()));
+                $exec = $callable . ($args === '' ? '' : ' '.$args);
+                if ($this->io->isVerbose()) {
+                    $this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec));
+                } else {
+                    $this->io->writeError(sprintf('> %s', $exec));
+                }
+                if (0 !== ($exitCode = $this->process->execute($exec))) {
                     $this->io->writeError(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
 
                     throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@@ -195,12 +202,18 @@ class EventDispatcher
     {
         $event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
 
+        if ($this->io->isVerbose()) {
+            $this->io->writeError(sprintf('> %s: %s::%s', $event->getName(), $className, $methodName));
+        } else {
+            $this->io->writeError(sprintf('> %s::%s', $className, $methodName));
+        }
+
         return $className::$methodName($event);
     }
 
     /**
-     * @param mixed $target
-     * @param Event $event
+     * @param  mixed              $target
+     * @param  Event              $event
      * @return Event|CommandEvent
      */
     protected function checkListenerExpectedEvent($target, Event $event)
@@ -247,7 +260,7 @@ class EventDispatcher
      *
      * @param string   $eventName The event name - typically a constant
      * @param Callable $listener  A callable expecting an event argument
-     * @param integer  $priority  A higher value represents a higher priority
+     * @param int      $priority  A higher value represents a higher priority
      */
     protected function addListener($eventName, $listener, $priority = 0)
     {
@@ -300,8 +313,8 @@ class EventDispatcher
     /**
      * Checks if an event has listeners registered
      *
-     * @param  Event   $event
-     * @return boolean
+     * @param  Event $event
+     * @return bool
      */
     public function hasEventListeners(Event $event)
     {
@@ -342,8 +355,8 @@ class EventDispatcher
     /**
      * Checks if string given references a class path and method
      *
-     * @param  string  $callable
-     * @return boolean
+     * @param  string $callable
+     * @return bool
      */
     protected function isPhpScript($callable)
     {

+ 7 - 3
src/Composer/Factory.php

@@ -16,6 +16,7 @@ use Composer\Config\JsonConfigSource;
 use Composer\Json\JsonFile;
 use Composer\IO\IOInterface;
 use Composer\Package\Archiver;
+use Composer\Package\Version\VersionGuesser;
 use Composer\Repository\RepositoryManager;
 use Composer\Repository\WritableRepositoryInterface;
 use Composer\Util\ProcessExecutor;
@@ -23,7 +24,7 @@ use Composer\Util\RemoteFilesystem;
 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
 use Composer\EventDispatcher\EventDispatcher;
 use Composer\Autoload\AutoloadGenerator;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\VersionParser;
 
 /**
  * Creates a configured instance of composer.
@@ -373,7 +374,8 @@ class Factory
 
         // load package
         $parser = new VersionParser;
-        $loader  = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io));
+        $guesser = new VersionGuesser($config, new ProcessExecutor($io), $parser);
+        $loader  = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser);
         $package = $loader->load($localConfig);
         $composer->setPackage($package);
 
@@ -415,7 +417,7 @@ class Factory
             $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
                 ? substr($composerFile, 0, -4).'lock'
                 : $composerFile . '.lock';
-            $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
+            $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, file_get_contents($composerFile));
             $composer->setLocker($locker);
         }
 
@@ -440,6 +442,7 @@ class Factory
         $rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository');
         $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository');
         $rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository');
+        $rm->setRepositoryClass('path', 'Composer\Repository\PathRepository');
 
         return $rm;
     }
@@ -512,6 +515,7 @@ class Factory
         $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
         $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache));
         $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache));
+        $dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $eventDispatcher, $cache));
 
         return $dm;
     }

+ 2 - 2
src/Composer/IO/BufferIO.php

@@ -23,8 +23,8 @@ use Symfony\Component\Console\Helper\HelperSet;
 class BufferIO extends ConsoleIO
 {
     /**
-     * @param string $input
-     * @param int $verbosity
+     * @param string                   $input
+     * @param int                      $verbosity
      * @param OutputFormatterInterface $formatter
      */
     public function __construct(

+ 5 - 6
src/Composer/IO/ConsoleIO.php

@@ -18,7 +18,6 @@ use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Helper\HelperSet;
 use Symfony\Component\Console\Question\ConfirmationQuestion;
 use Symfony\Component\Console\Question\Question;
-use Symfony\Component\Process\ExecutableFinder;
 
 /**
  * The Input/Output helper.
@@ -112,8 +111,8 @@ class ConsoleIO extends BaseIO
 
     /**
      * @param array $messages
-     * @param boolean $newline
-     * @param boolean $stderr
+     * @param bool  $newline
+     * @param bool  $stderr
      */
     private function doWrite($messages, $newline, $stderr)
     {
@@ -154,9 +153,9 @@ class ConsoleIO extends BaseIO
 
     /**
      * @param array $messages
-     * @param boolean $newline
-     * @param integer $size
-     * @param boolean $stderr
+     * @param bool  $newline
+     * @param int   $size
+     * @param bool  $stderr
      */
     private function doOverwrite($messages, $newline, $size, $stderr)
     {

+ 6 - 8
src/Composer/IO/IOInterface.php

@@ -77,7 +77,7 @@ interface IOInterface
      *
      * @param string|array $messages The message as an array of lines or a single string
      * @param bool         $newline  Whether to add a newline or not
-     * @param integer      $size     The size of line
+     * @param int          $size     The size of line
      */
     public function overwrite($messages, $newline = true, $size = null);
 
@@ -86,7 +86,7 @@ interface IOInterface
      *
      * @param string|array $messages The message as an array of lines or a single string
      * @param bool         $newline  Whether to add a newline or not
-     * @param integer      $size     The size of line
+     * @param int          $size     The size of line
      */
     public function overwriteError($messages, $newline = true, $size = null);
 
@@ -96,9 +96,8 @@ interface IOInterface
      * @param string|array $question The question to ask
      * @param string       $default  The default answer if none is given by the user
      *
-     * @return string The user answer
-     *
      * @throws \RuntimeException If there is no data to read in the input stream
+     * @return string            The user answer
      */
     public function ask($question, $default = null);
 
@@ -123,12 +122,11 @@ interface IOInterface
      *
      * @param string|array $question  The question to ask
      * @param callback     $validator A PHP callback
-     * @param null|integer $attempts  Max number of times to ask before giving up (default of null means infinite)
+     * @param null|int     $attempts  Max number of times to ask before giving up (default of null means infinite)
      * @param string       $default   The default answer if none is given by the user
      *
-     * @return mixed
-     *
      * @throws \Exception When any of the validators return an error
+     * @return mixed
      */
     public function askAndValidate($question, $validator, $attempts = null, $default = null);
 
@@ -153,7 +151,7 @@ interface IOInterface
      *
      * @param string $repositoryName The unique name of repository
      *
-     * @return boolean
+     * @return bool
      */
     public function hasAuthentication($repositoryName);
 

+ 133 - 29
src/Composer/Installer.php

@@ -18,6 +18,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
 use Composer\DependencyResolver\Operation\InstallOperation;
 use Composer\DependencyResolver\Operation\UninstallOperation;
 use Composer\DependencyResolver\Operation\OperationInterface;
+use Composer\DependencyResolver\PolicyInterface;
 use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Request;
 use Composer\DependencyResolver\Rule;
@@ -33,7 +34,7 @@ use Composer\Json\JsonFile;
 use Composer\Package\AliasPackage;
 use Composer\Package\CompletePackage;
 use Composer\Package\Link;
-use Composer\Package\LinkConstraint\VersionConstraint;
+use Composer\Semver\Constraint\Constraint;
 use Composer\Package\Locker;
 use Composer\Package\PackageInterface;
 use Composer\Package\RootPackageInterface;
@@ -43,6 +44,7 @@ use Composer\Repository\InstalledFilesystemRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryInterface;
 use Composer\Repository\RepositoryManager;
+use Composer\Repository\WritableRepositoryInterface;
 use Composer\Script\ScriptEvents;
 
 /**
@@ -101,6 +103,7 @@ class Installer
     protected $preferSource = false;
     protected $preferDist = false;
     protected $optimizeAutoloader = false;
+    protected $classMapAuthoritative = false;
     protected $devMode = false;
     protected $dryRun = false;
     protected $verbose = false;
@@ -157,9 +160,8 @@ class Installer
     /**
      * Run installation (or update)
      *
-     * @return int 0 on success or a positive error code on failure
-     *
      * @throws \Exception
+     * @return int        0 on success or a positive error code on failure
      */
     public function run()
     {
@@ -333,6 +335,7 @@ class Installer
                 }
 
                 $this->autoloadGenerator->setDevMode($this->devMode);
+                $this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative);
                 $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
             }
 
@@ -344,13 +347,23 @@ class Installer
 
             $vendorDir = $this->config->get('vendor-dir');
             if (is_dir($vendorDir)) {
-                touch($vendorDir);
+                // suppress errors as this fails sometimes on OSX for no apparent reason
+                // see https://github.com/composer/composer/issues/4070#issuecomment-129792748
+                @touch($vendorDir);
             }
         }
 
         return 0;
     }
 
+    /**
+     * @param  RepositoryInterface $localRepo
+     * @param  RepositoryInterface $installedRepo
+     * @param  PlatformRepository  $platformRepo
+     * @param  array               $aliases
+     * @param  bool                $withDevReqs
+     * @return int
+     */
     protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs)
     {
         // init vars
@@ -414,7 +427,7 @@ class Installer
                     && $this->installationManager->isPackageInstalled($localRepo, $package)
                 ) {
                     $removedUnstablePackages[$package->getName()] = true;
-                    $request->remove($package->getName(), new VersionConstraint('=', $package->getVersion()));
+                    $request->remove($package->getName(), new Constraint('=', $package->getVersion()));
                 }
             }
         }
@@ -453,7 +466,7 @@ class Installer
                     foreach ($currentPackages as $curPackage) {
                         if ($curPackage->getName() === $candidate) {
                             if (!$this->isUpdateable($curPackage) && !isset($removedUnstablePackages[$curPackage->getName()])) {
-                                $constraint = new VersionConstraint('=', $curPackage->getVersion());
+                                $constraint = new Constraint('=', $curPackage->getVersion());
                                 $request->install($curPackage->getName(), $constraint);
                             }
                             break;
@@ -473,7 +486,7 @@ class Installer
                 if (isset($aliases[$package->getName()][$version])) {
                     $version = $aliases[$package->getName()][$version]['alias_normalized'];
                 }
-                $constraint = new VersionConstraint('=', $version);
+                $constraint = new Constraint('=', $version);
                 $constraint->setPrettyString($package->getPrettyVersion());
                 $request->install($package->getName(), $constraint);
             }
@@ -511,6 +524,11 @@ class Installer
             return max(1, $e->getCode());
         }
 
+        if ($this->io->isVerbose()) {
+            $this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies");
+            $this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies");
+        }
+
         // force dev packages to be updated if we update or install from a (potentially new) lock
         $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, 'force-updates', $operations);
 
@@ -552,7 +570,8 @@ class Installer
                 if ('update' === $operation->getJobType()
                     && $operation->getTargetPackage()->isDev()
                     && $operation->getTargetPackage()->getVersion() === $operation->getInitialPackage()->getVersion()
-                    && $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference()
+                    && (!$operation->getTargetPackage()->getSourceReference() || $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference())
+                    && (!$operation->getTargetPackage()->getDistReference() || $operation->getTargetPackage()->getDistReference() === $operation->getInitialPackage()->getDistReference())
                 ) {
                     if ($this->io->isDebug()) {
                         $this->io->writeError('  - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
@@ -608,7 +627,7 @@ class Installer
 
         if (!$this->dryRun) {
             // force source/dist urls to be updated for all packages
-            $operations = $this->processPackageUrls($pool, $policy, $localRepo, $repositories);
+            $this->processPackageUrls($pool, $policy, $localRepo, $repositories);
             $localRepo->write();
         }
 
@@ -679,6 +698,11 @@ class Installer
         return array_merge($uninstOps, $operations);
     }
 
+    /**
+     * @param  bool                     $withDevReqs
+     * @param  RepositoryInterface|null $lockedRepository
+     * @return Pool
+     */
     private function createPool($withDevReqs, RepositoryInterface $lockedRepository = null)
     {
         if (!$this->update && $this->locker->isLocked()) { // install from lock
@@ -687,7 +711,7 @@ class Installer
 
             $requires = array();
             foreach ($lockedRepository->getPackages() as $package) {
-                $constraint = new VersionConstraint('=', $package->getVersion());
+                $constraint = new Constraint('=', $package->getVersion());
                 $constraint->setPrettyString($package->getPrettyVersion());
                 $requires[$package->getName()] = $constraint;
             }
@@ -717,6 +741,9 @@ class Installer
         return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
     }
 
+    /**
+     * @return DefaultPolicy
+     */
     private function createPolicy()
     {
         $preferStable = null;
@@ -737,11 +764,16 @@ class Installer
         return new DefaultPolicy($preferStable, $preferLowest);
     }
 
+    /**
+     * @param  RootPackageInterface $rootPackage
+     * @param  PlatformRepository   $platformRepo
+     * @return Request
+     */
     private function createRequest(RootPackageInterface $rootPackage, PlatformRepository $platformRepo)
     {
         $request = new Request();
 
-        $constraint = new VersionConstraint('=', $rootPackage->getVersion());
+        $constraint = new Constraint('=', $rootPackage->getVersion());
         $constraint->setPrettyString($rootPackage->getPrettyVersion());
         $request->install($rootPackage->getName(), $constraint);
 
@@ -755,7 +787,7 @@ class Installer
         // to prevent the solver trying to remove or update those
         $provided = $rootPackage->getProvides();
         foreach ($fixedPackages as $package) {
-            $constraint = new VersionConstraint('=', $package->getVersion());
+            $constraint = new Constraint('=', $package->getVersion());
             $constraint->setPrettyString($package->getPrettyVersion());
 
             // skip platform packages that are provided by the root package
@@ -770,6 +802,19 @@ class Installer
         return $request;
     }
 
+    /**
+     * @param  WritableRepositoryInterface $localRepo
+     * @param  Pool                        $pool
+     * @param  PolicyInterface             $policy
+     * @param  array                       $repositories
+     * @param  RepositoryInterface         $installedRepo
+     * @param  RepositoryInterface         $lockedRepository
+     * @param  bool                        $installFromLock
+     * @param  bool                        $withDevReqs
+     * @param  string                      $task
+     * @param  array|null                  $operations
+     * @return array
+     */
     private function processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, $task, array $operations = null)
     {
         if ($task === 'force-updates' && null === $operations) {
@@ -847,7 +892,7 @@ class Installer
                     }
 
                     // find similar packages (name/version) in all repositories
-                    $matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion()));
+                    $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
                     foreach ($matches as $index => $match) {
                         // skip local packages
                         if (!in_array($match->getRepository(), $repositories, true)) {
@@ -902,6 +947,9 @@ class Installer
 
     /**
      * Loads the most "current" list of packages that are installed meaning from lock ideally or from installed repo as fallback
+     * @param  bool                $withDevReqs
+     * @param  RepositoryInterface $installedRepo
+     * @return array
      */
     private function getCurrentPackages($withDevReqs, $installedRepo)
     {
@@ -917,6 +965,9 @@ class Installer
         return $installedRepo->getPackages();
     }
 
+    /**
+     * @return array
+     */
     private function getRootAliases()
     {
         if (!$this->update && $this->locker->isLocked()) {
@@ -930,13 +981,19 @@ class Installer
         foreach ($aliases as $alias) {
             $normalizedAliases[$alias['package']][$alias['version']] = array(
                 'alias' => $alias['alias'],
-                'alias_normalized' => $alias['alias_normalized']
+                'alias_normalized' => $alias['alias_normalized'],
             );
         }
 
         return $normalizedAliases;
     }
 
+    /**
+     * @param Pool                        $pool
+     * @param PolicyInterface             $policy
+     * @param WritableRepositoryInterface $localRepo
+     * @param array                       $repositories
+     */
     private function processPackageUrls($pool, $policy, $localRepo, $repositories)
     {
         if (!$this->update) {
@@ -945,7 +1002,7 @@ class Installer
 
         foreach ($localRepo->getCanonicalPackages() as $package) {
             // find similar packages (name/version) in all repositories
-            $matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion()));
+            $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
             foreach ($matches as $index => $match) {
                 // skip local packages
                 if (!in_array($match->getRepository(), $repositories, true)) {
@@ -967,7 +1024,15 @@ class Installer
                 $newPackage = $pool->literalToPackage($matches[0]);
 
                 // update the dist and source URLs
-                $package->setSourceUrl($newPackage->getSourceUrl());
+                $sourceUrl = $package->getSourceUrl();
+                $newSourceUrl = $newPackage->getSourceUrl();
+
+                if ($sourceUrl !== $newSourceUrl) {
+                    $package->setSourceType($newPackage->getSourceType());
+                    $package->setSourceUrl($newSourceUrl);
+                    $package->setSourceReference($newPackage->getSourceReference());
+                }
+
                 // only update dist url for github/bitbucket dists as they use a combination of dist url + dist reference to install
                 // but for other urls this is ambiguous and could result in bad outcomes
                 if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com)/}', $newPackage->getDistUrl())) {
@@ -977,6 +1042,10 @@ class Installer
         }
     }
 
+    /**
+     * @param PlatformRepository $platformRepo
+     * @param array              $aliases
+     */
     private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases)
     {
         foreach ($aliases as $package => $versions) {
@@ -991,6 +1060,10 @@ class Installer
         }
     }
 
+    /**
+     * @param  PackageInterface $package
+     * @return bool
+     */
     private function isUpdateable(PackageInterface $package)
     {
         if (!$this->updateWhitelist) {
@@ -1020,6 +1093,10 @@ class Installer
         return "{^" . $cleanedWhiteListedPattern . "$}i";
     }
 
+    /**
+     * @param  array $links
+     * @return array
+     */
     private function extractPlatformRequirements($links)
     {
         $platformReqs = array();
@@ -1040,7 +1117,7 @@ class Installer
      * update whitelist themselves.
      *
      * @param RepositoryInterface $localRepo
-     * @param boolean             $devMode
+     * @param bool                $devMode
      * @param array               $rootRequires    An array of links to packages in require of the root package
      * @param array               $rootDevRequires An array of links to packages in require-dev of the root package
      */
@@ -1172,6 +1249,10 @@ class Installer
         );
     }
 
+    /**
+     * @param  RepositoryInterface $additionalInstalledRepository
+     * @return $this
+     */
     public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository)
     {
         $this->additionalInstalledRepository = $additionalInstalledRepository;
@@ -1182,7 +1263,7 @@ class Installer
     /**
      * Whether to run in drymode or not
      *
-     * @param  boolean   $dryRun
+     * @param  bool      $dryRun
      * @return Installer
      */
     public function setDryRun($dryRun = true)
@@ -1205,7 +1286,7 @@ class Installer
     /**
      * prefer source installation
      *
-     * @param  boolean   $preferSource
+     * @param  bool      $preferSource
      * @return Installer
      */
     public function setPreferSource($preferSource = true)
@@ -1218,7 +1299,7 @@ class Installer
     /**
      * prefer dist installation
      *
-     * @param  boolean   $preferDist
+     * @param  bool      $preferDist
      * @return Installer
      */
     public function setPreferDist($preferDist = true)
@@ -1237,6 +1318,29 @@ class Installer
     public function setOptimizeAutoloader($optimizeAutoloader = false)
     {
         $this->optimizeAutoloader = (boolean) $optimizeAutoloader;
+        if (!$this->optimizeAutoloader) {
+            // Force classMapAuthoritative off when not optimizing the
+            // autoloader
+            $this->setClassMapAuthoritative(false);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Whether or not generated autoloader considers the class map
+     * authoritative.
+     *
+     * @param  bool      $classMapAuthoritative
+     * @return Installer
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative = false)
+    {
+        $this->classMapAuthoritative = (boolean) $classMapAuthoritative;
+        if ($this->classMapAuthoritative) {
+            // Force optimizeAutoloader when classmap is authoritative
+            $this->setOptimizeAutoloader(true);
+        }
 
         return $this;
     }
@@ -1244,7 +1348,7 @@ class Installer
     /**
      * update packages
      *
-     * @param  boolean   $update
+     * @param  bool      $update
      * @return Installer
      */
     public function setUpdate($update = true)
@@ -1257,7 +1361,7 @@ class Installer
     /**
      * enables dev packages
      *
-     * @param  boolean   $devMode
+     * @param  bool      $devMode
      * @return Installer
      */
     public function setDevMode($devMode = true)
@@ -1270,7 +1374,7 @@ class Installer
     /**
      * set whether to run autoloader or not
      *
-     * @param boolean $dumpAutoloader
+     * @param  bool      $dumpAutoloader
      * @return Installer
      */
     public function setDumpAutoloader($dumpAutoloader = true)
@@ -1283,7 +1387,7 @@ class Installer
     /**
      * set whether to run scripts or not
      *
-     * @param  boolean   $runScripts
+     * @param  bool      $runScripts
      * @return Installer
      */
     public function setRunScripts($runScripts = true)
@@ -1309,7 +1413,7 @@ class Installer
     /**
      * run in verbose mode
      *
-     * @param  boolean   $verbose
+     * @param  bool      $verbose
      * @return Installer
      */
     public function setVerbose($verbose = true)
@@ -1332,7 +1436,7 @@ class Installer
     /**
      * set ignore Platform Package requirements
      *
-     * @param  boolean   $ignorePlatformReqs
+     * @param  bool      $ignorePlatformReqs
      * @return Installer
      */
     public function setIgnorePlatformRequirements($ignorePlatformReqs = false)
@@ -1359,7 +1463,7 @@ class Installer
     /**
      * Should dependencies of whitelisted packages be updated recursively?
      *
-     * @param  boolean   $updateDependencies
+     * @param  bool      $updateDependencies
      * @return Installer
      */
     public function setWhitelistDependencies($updateDependencies = true)
@@ -1372,7 +1476,7 @@ class Installer
     /**
      * Should packages be preferred in a stable version when updating?
      *
-     * @param  boolean   $preferStable
+     * @param  bool      $preferStable
      * @return Installer
      */
     public function setPreferStable($preferStable = true)
@@ -1385,7 +1489,7 @@ class Installer
     /**
      * Should packages be preferred in a lowest version when updating?
      *
-     * @param  boolean   $preferLowest
+     * @param  bool      $preferLowest
      * @return Installer
      */
     public function setPreferLowest($preferLowest = true)

+ 3 - 4
src/Composer/Installer/InstallationManager.php

@@ -89,9 +89,8 @@ class InstallationManager
      *
      * @param string $type package type
      *
-     * @return InstallerInterface
-     *
      * @throws \InvalidArgumentException if installer for provided type is not registered
+     * @return InstallerInterface
      */
     public function getInstaller($type)
     {
@@ -249,7 +248,7 @@ class InstallationManager
                             'header'  => array('Content-type: application/x-www-form-urlencoded'),
                             'content' => http_build_query($params, '', '&'),
                             'timeout' => 3,
-                        )
+                        ),
                     );
 
                     $context = StreamContextFactory::getContext($url, $opts);
@@ -273,7 +272,7 @@ class InstallationManager
                     'header'  => array('Content-Type: application/json'),
                     'content' => json_encode($postData),
                     'timeout' => 6,
-                )
+                ),
             );
 
             $context = StreamContextFactory::getContext($repoUrl, $opts);

+ 1 - 0
src/Composer/Installer/InstallerInterface.php

@@ -14,6 +14,7 @@ namespace Composer\Installer;
 
 use Composer\Package\PackageInterface;
 use Composer\Repository\InstalledRepositoryInterface;
+use InvalidArgumentException;
 
 /**
  * Interface for the package installation manager.

+ 5 - 0
src/Composer/Installer/LibraryInstaller.php

@@ -266,6 +266,11 @@ class LibraryInstaller implements InstallerInterface
                 $this->filesystem->unlink($link.'.bat');
             }
         }
+
+        // attempt removing the bin dir in case it is left empty
+        if ((is_dir($this->binDir)) && ($this->filesystem->isDirEmpty($this->binDir))) {
+            @rmdir($this->binDir);
+        }
     }
 
     protected function initializeVendorDir()

+ 0 - 1
src/Composer/Installer/PackageEvent.php

@@ -18,7 +18,6 @@ use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\DependencyResolver\PolicyInterface;
 use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Request;
-use Composer\EventDispatcher\Event;
 use Composer\Repository\CompositeRepository;
 
 /**

+ 0 - 2
src/Composer/Installer/PluginInstaller.php

@@ -13,7 +13,6 @@
 namespace Composer\Installer;
 
 use Composer\Composer;
-use Composer\Package\Package;
 use Composer\IO\IOInterface;
 use Composer\Repository\InstalledRepositoryInterface;
 use Composer\Package\PackageInterface;
@@ -27,7 +26,6 @@ use Composer\Package\PackageInterface;
 class PluginInstaller extends LibraryInstaller
 {
     private $installationManager;
-    private static $classCounter = 0;
 
     /**
      * Initializes Plugin installer.

+ 3 - 3
src/Composer/Json/JsonFile.php

@@ -137,8 +137,8 @@ class JsonFile
      * Validates the schema of the current json file according to composer-schema.json rules
      *
      * @param  int                     $schema a JsonFile::*_SCHEMA constant
-     * @return bool                    true on success
      * @throws JsonValidationException
+     * @return bool                    true on success
      */
     public function validateSchema($schema = self::STRICT_SCHEMA)
     {
@@ -218,7 +218,7 @@ class JsonFile
     /**
      * Throws an exception according to a given code with a customized message
      *
-     * @param int $code return code of json_last_error function
+     * @param  int               $code return code of json_last_error function
      * @throws \RuntimeException
      */
     private static function throwEncodeError($code)
@@ -269,10 +269,10 @@ class JsonFile
      *
      * @param  string                    $json
      * @param  string                    $file
-     * @return bool                      true on success
      * @throws \UnexpectedValueException
      * @throws JsonValidationException
      * @throws ParsingException
+     * @return bool                      true on success
      */
     protected static function validateSyntax($json, $file = null)
     {

+ 0 - 1
src/Composer/Json/JsonFormatter.php

@@ -23,7 +23,6 @@ namespace Composer\Json;
 class JsonFormatter
 {
     /**
-     *
      * This code is based on the function found at:
      *  http://recursive-design.com/blog/2008/03/11/format-json-with-php/
      *

+ 5 - 5
src/Composer/Package/AliasPackage.php

@@ -12,8 +12,8 @@
 
 namespace Composer\Package;
 
-use Composer\Package\LinkConstraint\VersionConstraint;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\Constraint\Constraint;
+use Composer\Semver\VersionParser;
 
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
@@ -162,7 +162,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
     }
 
     /**
-     * @param array $links
+     * @param array  $links
      * @param string $linkType
      * @internal param string $prettyVersion
      * @return array
@@ -174,14 +174,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
             foreach ($links as $link) {
                 // link is self.version, but must be replacing also the replaced version
                 if ('self.version' === $link->getPrettyConstraint()) {
-                    $newLinks[] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $this->version), $linkType, $this->prettyVersion);
+                    $newLinks[] = new Link($link->getSource(), $link->getTarget(), new Constraint('=', $this->version), $linkType, $this->prettyVersion);
                 }
             }
             $links = array_merge($links, $newLinks);
         } else {
             foreach ($links as $index => $link) {
                 if ('self.version' === $link->getPrettyConstraint()) {
-                    $links[$index] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $this->version), $linkType, $this->prettyVersion);
+                    $links[$index] = new Link($link->getSource(), $link->getTarget(), new Constraint('=', $this->version), $linkType, $this->prettyVersion);
                 }
             }
         }

+ 3 - 4
src/Composer/Package/Archiver/ArchivableFilesFinder.php

@@ -13,8 +13,7 @@
 namespace Composer\Package\Archiver;
 
 use Composer\Util\Filesystem;
-
-use Symfony\Component\Finder;
+use Symfony\Component\Finder\Finder;
 
 /**
  * A Symfony Finder wrapper which locates files that should go into archives
@@ -27,7 +26,7 @@ use Symfony\Component\Finder;
 class ArchivableFilesFinder extends \FilterIterator
 {
     /**
-     * @var Symfony\Component\Finder\Finder
+     * @var Finder
      */
     protected $finder;
 
@@ -49,7 +48,7 @@ class ArchivableFilesFinder extends \FilterIterator
             new ComposerExcludeFilter($sources, $excludes),
         );
 
-        $this->finder = new Finder\Finder();
+        $this->finder = new Finder();
 
         $filter = function (\SplFileInfo $file) use ($sources, $filters, $fs) {
             if ($file->isLink() && strpos($file->getLinkTarget(), $sources) !== 0) {

+ 1 - 1
src/Composer/Package/Archiver/ArchiverInterface.php

@@ -37,7 +37,7 @@ interface ArchiverInterface
      * @param string $format     The archive format
      * @param string $sourceType The source type (git, svn, hg, etc.)
      *
-     * @return boolean true if the format is supported by the archiver
+     * @return bool true if the format is supported by the archiver
      */
     public function supports($format, $sourceType);
 }

+ 2 - 2
src/Composer/Package/Archiver/BaseExcludeFilter.php

@@ -123,7 +123,7 @@ abstract class BaseExcludeFilter
     protected function generatePattern($rule)
     {
         $negate = false;
-        $pattern = '#';
+        $pattern = '{';
 
         if (strlen($rule) && $rule[0] === '!') {
             $negate = true;
@@ -143,6 +143,6 @@ abstract class BaseExcludeFilter
         // remove delimiters as well as caret (^) and dollar sign ($) from the regex
         $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '(?=$|/)';
 
-        return array($pattern . '#', $negate, false);
+        return array($pattern . '}', $negate, false);
     }
 }

+ 1 - 1
src/Composer/Package/Archiver/HgExcludeFilter.php

@@ -26,7 +26,7 @@ class HgExcludeFilter extends BaseExcludeFilter
 
     /**
      * Either HG_IGNORE_REGEX or HG_IGNORE_GLOB
-     * @var integer
+     * @var int
      */
     protected $patternMode;
 

+ 18 - 1
src/Composer/Package/BasePackage.php

@@ -161,7 +161,7 @@ abstract class BasePackage implements PackageInterface
     /**
      * checks if this package is a platform package
      *
-     * @return boolean
+     * @return bool
      */
     public function isPlatform()
     {
@@ -206,6 +206,23 @@ abstract class BasePackage implements PackageInterface
         return $this->getPrettyName().' '.$this->getPrettyVersion();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function getFullPrettyVersion($truncate = true)
+    {
+        if (!$this->isDev() || !in_array($this->getSourceType(), array('hg', 'git'))) {
+            return $this->getPrettyVersion();
+        }
+
+        // if source reference is a sha1 hash -- truncate
+        if ($truncate && strlen($this->getSourceReference()) === 40) {
+            return $this->getPrettyVersion() . ' ' . substr($this->getSourceReference(), 0, 7);
+        }
+
+        return $this->getPrettyVersion() . ' ' . $this->getSourceReference();
+    }
+
     public function __clone()
     {
         $this->repository = null;

+ 2 - 2
src/Composer/Package/CompletePackage.php

@@ -172,7 +172,7 @@ class CompletePackage extends Package implements CompletePackageInterface
     }
 
     /**
-     * @return boolean
+     * @return bool
      */
     public function isAbandoned()
     {
@@ -180,7 +180,7 @@ class CompletePackage extends Package implements CompletePackageInterface
     }
 
     /**
-     * @param boolean|string $abandoned
+     * @param bool|string $abandoned
      */
     public function setAbandoned($abandoned)
     {

+ 1 - 1
src/Composer/Package/CompletePackageInterface.php

@@ -82,7 +82,7 @@ interface CompletePackageInterface extends PackageInterface
     /**
      * Returns if the package is abandoned or not
      *
-     * @return boolean
+     * @return bool
      */
     public function isAbandoned();
 

+ 10 - 10
src/Composer/Package/Link.php

@@ -12,7 +12,7 @@
 
 namespace Composer\Package;
 
-use Composer\Package\LinkConstraint\LinkConstraintInterface;
+use Composer\Semver\Constraint\ConstraintInterface;
 
 /**
  * Represents a link between two packages, represented by their names
@@ -32,7 +32,7 @@ class Link
     protected $target;
 
     /**
-     * @var LinkConstraintInterface|null
+     * @var ConstraintInterface|null
      */
     protected $constraint;
 
@@ -49,13 +49,13 @@ class Link
     /**
      * Creates a new package link.
      *
-     * @param string                       $source
-     * @param string                       $target
-     * @param LinkConstraintInterface|null $constraint       Constraint applying to the target of this link
-     * @param string                       $description      Used to create a descriptive string representation
-     * @param string|null                  $prettyConstraint
+     * @param string                   $source
+     * @param string                   $target
+     * @param ConstraintInterface|null $constraint       Constraint applying to the target of this link
+     * @param string                   $description      Used to create a descriptive string representation
+     * @param string|null              $prettyConstraint
      */
-    public function __construct($source, $target, LinkConstraintInterface $constraint = null, $description = 'relates to', $prettyConstraint = null)
+    public function __construct($source, $target, ConstraintInterface $constraint = null, $description = 'relates to', $prettyConstraint = null)
     {
         $this->source = strtolower($source);
         $this->target = strtolower($target);
@@ -81,7 +81,7 @@ class Link
     }
 
     /**
-     * @return LinkConstraintInterface|null
+     * @return ConstraintInterface|null
      */
     public function getConstraint()
     {
@@ -110,7 +110,7 @@ class Link
     }
 
     /**
-     * @param PackageInterface $sourcePackage
+     * @param  PackageInterface $sourcePackage
      * @return string
      */
     public function getPrettyString(PackageInterface $sourcePackage)

+ 6 - 29
src/Composer/Package/LinkConstraint/EmptyConstraint.php

@@ -12,36 +12,13 @@
 
 namespace Composer\Package\LinkConstraint;
 
+use Composer\Semver\Constraint\EmptyConstraint as SemverEmptyConstraint;
+
+@trigger_error('The ' . __NAMESPACE__ . '\EmptyConstraint class is deprecated, use Composer\Semver\Constraint\EmptyConstraint instead.', E_USER_DEPRECATED);
+
 /**
- * Defines an absence of constraints
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @deprecated use Composer\Semver\Constraint\EmptyConstraint instead
  */
-class EmptyConstraint implements LinkConstraintInterface
+class EmptyConstraint extends SemverEmptyConstraint implements LinkConstraintInterface
 {
-    protected $prettyString;
-
-    public function matches(LinkConstraintInterface $provider)
-    {
-        return true;
-    }
-
-    public function setPrettyString($prettyString)
-    {
-        $this->prettyString = $prettyString;
-    }
-
-    public function getPrettyString()
-    {
-        if ($this->prettyString) {
-            return $this->prettyString;
-        }
-
-        return $this->__toString();
-    }
-
-    public function __toString()
-    {
-        return '[]';
-    }
 }

+ 6 - 8
src/Composer/Package/LinkConstraint/LinkConstraintInterface.php

@@ -12,15 +12,13 @@
 
 namespace Composer\Package\LinkConstraint;
 
+use Composer\Semver\Constraint\ConstraintInterface;
+
+@trigger_error('The ' . __NAMESPACE__ . '\LinkConstraintInterface interface is deprecated, use Composer\Semver\Constraint\ConstraintInterface instead.', E_USER_DEPRECATED);
+
 /**
- * Defines a constraint on a link between two packages.
- *
- * @author Nils Adermann <naderman@naderman.de>
+ * @deprecated use Composer\Semver\Constraint\ConstraintInterface instead
  */
-interface LinkConstraintInterface
+interface LinkConstraintInterface extends ConstraintInterface
 {
-    public function matches(LinkConstraintInterface $provider);
-    public function setPrettyString($prettyString);
-    public function getPrettyString();
-    public function __toString();
 }

+ 6 - 65
src/Composer/Package/LinkConstraint/MultiConstraint.php

@@ -12,72 +12,13 @@
 
 namespace Composer\Package\LinkConstraint;
 
+use Composer\Semver\Constraint\MultiConstraint as SemverMultiConstraint;
+
+@trigger_error('The ' . __NAMESPACE__ . '\MultiConstraint class is deprecated, use Composer\Semver\Constraint\MultiConstraint instead.', E_USER_DEPRECATED);
+
 /**
- * Defines a conjunctive or disjunctive set of constraints on the target of a package link
- *
- * @author Nils Adermann <naderman@naderman.de>
- * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @deprecated use Composer\Semver\Constraint\MultiConstraint instead
  */
-class MultiConstraint implements LinkConstraintInterface
+class MultiConstraint extends SemverMultiConstraint implements LinkConstraintInterface
 {
-    protected $constraints;
-    protected $prettyString;
-    protected $conjunctive;
-
-    /**
-     * Sets operator and version to compare a package with
-     *
-     * @param array $constraints A set of constraints
-     * @param bool  $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
-     */
-    public function __construct(array $constraints, $conjunctive = true)
-    {
-        $this->constraints = $constraints;
-        $this->conjunctive = $conjunctive;
-    }
-
-    public function matches(LinkConstraintInterface $provider)
-    {
-        if (false === $this->conjunctive) {
-            foreach ($this->constraints as $constraint) {
-                if ($constraint->matches($provider)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        foreach ($this->constraints as $constraint) {
-            if (!$constraint->matches($provider)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public function setPrettyString($prettyString)
-    {
-        $this->prettyString = $prettyString;
-    }
-
-    public function getPrettyString()
-    {
-        if ($this->prettyString) {
-            return $this->prettyString;
-        }
-
-        return $this->__toString();
-    }
-
-    public function __toString()
-    {
-        $constraints = array();
-        foreach ($this->constraints as $constraint) {
-            $constraints[] = $constraint->__toString();
-        }
-
-        return '['.implode($this->conjunctive ? ' ' : ' || ', $constraints).']';
-    }
 }

+ 6 - 35
src/Composer/Package/LinkConstraint/SpecificConstraint.php

@@ -12,42 +12,13 @@
 
 namespace Composer\Package\LinkConstraint;
 
+use Composer\Semver\Constraint\AbstractConstraint;
+
+@trigger_error('The ' . __NAMESPACE__ . '\SpecificConstraint abstract class is deprecated, use Composer\Semver\Constraint\AbstractConstraint instead.', E_USER_DEPRECATED);
+
 /**
- * Provides a common basis for specific package link constraints
- *
- * @author Nils Adermann <naderman@naderman.de>
+ * @deprecated use Composer\Semver\Constraint\AbstractConstraint instead
  */
-abstract class SpecificConstraint implements LinkConstraintInterface
+abstract class SpecificConstraint extends AbstractConstraint implements LinkConstraintInterface
 {
-    protected $prettyString;
-
-    public function matches(LinkConstraintInterface $provider)
-    {
-        if ($provider instanceof MultiConstraint) {
-            // turn matching around to find a match
-            return $provider->matches($this);
-        } elseif ($provider instanceof $this) {
-            return $this->matchSpecific($provider);
-        }
-
-        return true;
-    }
-
-    public function setPrettyString($prettyString)
-    {
-        $this->prettyString = $prettyString;
-    }
-
-    public function getPrettyString()
-    {
-        if ($this->prettyString) {
-            return $this->prettyString;
-        }
-
-        return $this->__toString();
-    }
-
-    // implementations must implement a method of this format:
-    // not declared abstract here because type hinting violates parameter coherence (TODO right word?)
-    // public function matchSpecific(<SpecificConstraintType> $provider);
 }

+ 6 - 106
src/Composer/Package/LinkConstraint/VersionConstraint.php

@@ -12,113 +12,13 @@
 
 namespace Composer\Package\LinkConstraint;
 
+use Composer\Semver\Constraint\Constraint;
+
+@trigger_error('The ' . __NAMESPACE__ . '\VersionConstraint class is deprecated, use Composer\Semver\Constraint\Constraint instead.', E_USER_DEPRECATED);
+
 /**
- * Constrains a package link based on package version
- *
- * Version numbers must be compatible with version_compare
- *
- * @author Nils Adermann <naderman@naderman.de>
+ * @deprecated use Composer\Semver\Constraint\Constraint instead
  */
-class VersionConstraint extends SpecificConstraint
+class VersionConstraint extends Constraint implements LinkConstraintInterface
 {
-    private $operator;
-    private $version;
-
-    /**
-     * Sets operator and version to compare a package with
-     *
-     * @param string $operator A comparison operator
-     * @param string $version  A version to compare to
-     */
-    public function __construct($operator, $version)
-    {
-        if ('=' === $operator) {
-            $operator = '==';
-        }
-
-        if ('<>' === $operator) {
-            $operator = '!=';
-        }
-
-        $this->operator = $operator;
-        $this->version = $version;
-    }
-
-    public function versionCompare($a, $b, $operator, $compareBranches = false)
-    {
-        $aIsBranch = 'dev-' === substr($a, 0, 4);
-        $bIsBranch = 'dev-' === substr($b, 0, 4);
-        if ($aIsBranch && $bIsBranch) {
-            return $operator == '==' && $a === $b;
-        }
-
-        // when branches are not comparable, we make sure dev branches never match anything
-        if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
-            return false;
-        }
-
-        return version_compare($a, $b, $operator);
-    }
-
-    /**
-     * @param  VersionConstraint $provider
-     * @param  bool              $compareBranches
-     * @return bool
-     */
-    public function matchSpecific(VersionConstraint $provider, $compareBranches = false)
-    {
-        static $cache = array();
-        if (isset($cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches])) {
-            return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches];
-        }
-
-        return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches] =
-            $this->doMatchSpecific($provider, $compareBranches);
-    }
-
-    /**
-     * @param  VersionConstraint $provider
-     * @param  bool              $compareBranches
-     * @return bool
-     */
-    private function doMatchSpecific(VersionConstraint $provider, $compareBranches = false)
-    {
-        $noEqualOp = str_replace('=', '', $this->operator);
-        $providerNoEqualOp = str_replace('=', '', $provider->operator);
-
-        $isEqualOp = '==' === $this->operator;
-        $isNonEqualOp = '!=' === $this->operator;
-        $isProviderEqualOp = '==' === $provider->operator;
-        $isProviderNonEqualOp = '!=' === $provider->operator;
-
-        // '!=' operator is match when other operator is not '==' operator or version is not match
-        // these kinds of comparisons always have a solution
-        if ($isNonEqualOp || $isProviderNonEqualOp) {
-            return !$isEqualOp && !$isProviderEqualOp
-                || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
-        }
-
-        // an example for the condition is <= 2.0 & < 1.0
-        // these kinds of comparisons always have a solution
-        if ($this->operator != '==' && $noEqualOp == $providerNoEqualOp) {
-            return true;
-        }
-
-        if ($this->versionCompare($provider->version, $this->version, $this->operator, $compareBranches)) {
-            // special case, e.g. require >= 1.0 and provide < 1.0
-            // 1.0 >= 1.0 but 1.0 is outside of the provided interval
-            if ($provider->version == $this->version && $provider->operator == $providerNoEqualOp && $this->operator != $noEqualOp) {
-                return false;
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    public function __toString()
-    {
-        return $this->operator.' '.$this->version;
-    }
 }

+ 27 - 3
src/Composer/Package/Loader/ArrayLoader.php

@@ -14,9 +14,10 @@ namespace Composer\Package\Loader;
 
 use Composer\Package;
 use Composer\Package\AliasPackage;
+use Composer\Package\Link;
 use Composer\Package\RootAliasPackage;
 use Composer\Package\RootPackageInterface;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\VersionParser;
 
 /**
  * @author Konstantin Kudryashiv <ever.zet@gmail.com>
@@ -115,7 +116,7 @@ class ArrayLoader implements LoaderInterface
             if (isset($config[$type])) {
                 $method = 'set'.ucfirst($opts['method']);
                 $package->{$method}(
-                    $this->versionParser->parseLinks(
+                    $this->parseLinks(
                         $package->getName(),
                         $package->getPrettyVersion(),
                         $opts['description'],
@@ -147,7 +148,7 @@ class ArrayLoader implements LoaderInterface
         }
 
         if (!empty($config['time'])) {
-            $time = preg_match('/^\d+$/D', $config['time']) ? '@'.$config['time'] : $config['time'];
+            $time = preg_match('/^\d++$/D', $config['time']) ? '@'.$config['time'] : $config['time'];
 
             try {
                 $date = new \DateTime($time, new \DateTimeZone('UTC'));
@@ -216,6 +217,29 @@ class ArrayLoader implements LoaderInterface
         return $package;
     }
 
+    /**
+     * @param  string $source        source package name
+     * @param  string $sourceVersion source package version (pretty version ideally)
+     * @param  string $description   link description (e.g. requires, replaces, ..)
+     * @param  array  $links         array of package name => constraint mappings
+     * @return Link[]
+     */
+    public function parseLinks($source, $sourceVersion, $description, $links)
+    {
+        $res = array();
+        foreach ($links as $target => $constraint) {
+            if ('self.version' === $constraint) {
+                $parsedConstraint = $this->versionParser->parseConstraints($sourceVersion);
+            } else {
+                $parsedConstraint = $this->versionParser->parseConstraints($constraint);
+            }
+
+            $res[strtolower($target)] = new Link($source, $target, $parsedConstraint, $description, $constraint);
+        }
+
+        return $res;
+    }
+
     /**
      * Retrieves a branch alias (dev-master => 1.0.x-dev for example) if it exists
      *

+ 19 - 178
src/Composer/Package/Loader/RootPackageLoader.php

@@ -16,13 +16,10 @@ use Composer\Package\BasePackage;
 use Composer\Package\AliasPackage;
 use Composer\Config;
 use Composer\Factory;
-use Composer\Package\Version\VersionParser;
+use Composer\Package\Version\VersionGuesser;
+use Composer\Semver\VersionParser;
 use Composer\Repository\RepositoryManager;
-use Composer\Repository\Vcs\HgDriver;
-use Composer\IO\NullIO;
 use Composer\Util\ProcessExecutor;
-use Composer\Util\Git as GitUtil;
-use Composer\Util\Svn as SvnUtil;
 
 /**
  * ArrayLoader built for the sole purpose of loading the root package
@@ -33,16 +30,28 @@ use Composer\Util\Svn as SvnUtil;
  */
 class RootPackageLoader extends ArrayLoader
 {
+    /**
+     * @var RepositoryManager
+     */
     private $manager;
+
+    /**
+     * @var Config
+     */
     private $config;
-    private $process;
 
-    public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, ProcessExecutor $process = null)
+    /**
+     * @var VersionGuesser
+     */
+    private $versionGuesser;
+
+    public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, VersionGuesser $versionGuesser = null)
     {
+        parent::__construct($parser);
+
         $this->manager = $manager;
         $this->config = $config;
-        $this->process = $process ?: new ProcessExecutor();
-        parent::__construct($parser);
+        $this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor(), $this->versionParser);
     }
 
     public function load(array $config, $class = 'Composer\Package\RootPackage')
@@ -56,7 +65,7 @@ class RootPackageLoader extends ArrayLoader
             if (getenv('COMPOSER_ROOT_VERSION')) {
                 $version = getenv('COMPOSER_ROOT_VERSION');
             } else {
-                $version = $this->guessVersion($config);
+                $version = $this->versionGuesser->guessVersion($config, getcwd());
             }
 
             if (!$version) {
@@ -176,172 +185,4 @@ class RootPackageLoader extends ArrayLoader
 
         return $references;
     }
-
-    private function guessVersion(array $config)
-    {
-        if (function_exists('proc_open')) {
-            $version = $this->guessGitVersion($config);
-            if (null !== $version) {
-                return $version;
-            }
-
-            $version = $this->guessHgVersion($config);
-            if (null !== $version) {
-                return $version;
-            }
-
-            return $this->guessSvnVersion($config);
-        }
-    }
-
-    private function guessGitVersion(array $config)
-    {
-        GitUtil::cleanEnv();
-
-        // try to fetch current version from git tags
-        if (0 === $this->process->execute('git describe --exact-match --tags', $output)) {
-            try {
-                return $this->versionParser->normalize(trim($output));
-            } catch (\Exception $e) {
-            }
-        }
-
-        // try to fetch current version from git branch
-        if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) {
-            $branches = array();
-            $isFeatureBranch = false;
-            $version = null;
-
-            // find current branch and collect all branch names
-            foreach ($this->process->splitLines($output) as $branch) {
-                if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
-                    if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ') {
-                        $version = 'dev-'.$match[2];
-                        $isFeatureBranch = true;
-                    } else {
-                        $version = $this->versionParser->normalizeBranch($match[1]);
-                        $isFeatureBranch = 0 === strpos($version, 'dev-');
-                        if ('9999999-dev' === $version) {
-                            $version = 'dev-'.$match[1];
-                        }
-                    }
-                }
-
-                if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
-                    if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
-                        $branches[] = $match[1];
-                    }
-                }
-            }
-
-            if (!$isFeatureBranch) {
-                return $version;
-            }
-
-            // try to find the best (nearest) version branch to assume this feature's version
-            $version = $this->guessFeatureVersion($config, $version, $branches, 'git rev-list %candidate%..%branch%');
-
-            return $version;
-        }
-    }
-
-    private function guessHgVersion(array $config)
-    {
-        // try to fetch current version from hg branch
-        if (0 === $this->process->execute('hg branch', $output)) {
-            $branch = trim($output);
-            $version = $this->versionParser->normalizeBranch($branch);
-            $isFeatureBranch = 0 === strpos($version, 'dev-');
-
-            if ('9999999-dev' === $version) {
-                $version = 'dev-'.$branch;
-            }
-
-            if (!$isFeatureBranch) {
-                return $version;
-            }
-
-            // re-use the HgDriver to fetch branches (this properly includes bookmarks)
-            $config = array('url' => getcwd());
-            $driver = new HgDriver($config, new NullIO(), $this->config, $this->process);
-            $branches = array_keys($driver->getBranches());
-
-            // try to find the best (nearest) version branch to assume this feature's version
-            $version = $this->guessFeatureVersion($config, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"');
-
-            return $version;
-        }
-    }
-
-    private function guessFeatureVersion(array $config, $version, array $branches, $scmCmdline)
-    {
-        // ignore feature branches if they have no branch-alias or self.version is used
-        // and find the branch they came from to use as a version instead
-        if ((isset($config['extra']['branch-alias']) && !isset($config['extra']['branch-alias'][$version]))
-            || strpos(json_encode($config), '"self.version"')
-        ) {
-            $branch = preg_replace('{^dev-}', '', $version);
-            $length = PHP_INT_MAX;
-
-            $nonFeatureBranches = '';
-            if (!empty($config['non-feature-branches'])) {
-                $nonFeatureBranches = implode('|', $config['non-feature-branches']);
-            }
-
-            foreach ($branches as $candidate) {
-                // return directly, if branch is configured to be non-feature branch
-                if ($candidate === $branch && preg_match('{^(' . $nonFeatureBranches . ')$}', $candidate)) {
-                    return $version;
-                }
-
-                // do not compare against other feature branches
-                if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
-                    continue;
-                }
-
-                $cmdLine = str_replace(array('%candidate%', '%branch%'), array($candidate, $branch), $scmCmdline);
-                if (0 !== $this->process->execute($cmdLine, $output)) {
-                    continue;
-                }
-
-                if (strlen($output) < $length) {
-                    $length = strlen($output);
-                    $version = $this->versionParser->normalizeBranch($candidate);
-                    if ('9999999-dev' === $version) {
-                        $version = 'dev-'.$match[1];
-                    }
-                }
-            }
-        }
-
-        return $version;
-    }
-
-    private function guessSvnVersion(array $config)
-    {
-        SvnUtil::cleanEnv();
-
-        // try to fetch current version from svn
-        if (0 === $this->process->execute('svn info --xml', $output)) {
-            $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk';
-            $branchesPath = isset($config['branches-path']) ? preg_quote($config['branches-path'], '#') : 'branches';
-            $tagsPath = isset($config['tags-path']) ? preg_quote($config['tags-path'], '#') : 'tags';
-
-            $urlPattern = '#<url>.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))</url>#';
-
-            if (preg_match($urlPattern, $output, $matches)) {
-                if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) {
-                    // we are in a branches path
-                    $version = $this->versionParser->normalizeBranch($matches[3]);
-                    if ('9999999-dev' === $version) {
-                        $version = 'dev-'.$matches[3];
-                    }
-
-                    return $version;
-                }
-
-                return $this->versionParser->normalize(trim($matches[1]));
-            }
-        }
-    }
 }

+ 3 - 3
src/Composer/Package/Loader/ValidatingArrayLoader.php

@@ -14,8 +14,8 @@ namespace Composer\Package\Loader;
 
 use Composer\Package;
 use Composer\Package\BasePackage;
-use Composer\Package\LinkConstraint\VersionConstraint;
-use Composer\Package\Version\VersionParser;
+use Composer\Semver\Constraint\Constraint;
+use Composer\Semver\VersionParser;
 use Composer\Repository\PlatformRepository;
 
 /**
@@ -149,7 +149,7 @@ class ValidatingArrayLoader implements LoaderInterface
             }
         }
 
-        $unboundConstraint = new VersionConstraint('=', $this->versionParser->normalize('dev-master'));
+        $unboundConstraint = new Constraint('=', $this->versionParser->normalize('dev-master'));
 
         foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) {
             if ($this->validateArray($linkType) && isset($this->config[$linkType])) {

+ 63 - 12
src/Composer/Package/Locker.php

@@ -19,9 +19,9 @@ use Composer\Util\ProcessExecutor;
 use Composer\Repository\ArrayRepository;
 use Composer\Package\Dumper\ArrayDumper;
 use Composer\Package\Loader\ArrayLoader;
-use Composer\Package\Version\VersionParser;
 use Composer\Util\Git as GitUtil;
 use Composer\IO\IOInterface;
+use Seld\JsonLint\ParsingException;
 
 /**
  * Reads/writes project lockfile (composer.lock).
@@ -35,6 +35,7 @@ class Locker
     private $repositoryManager;
     private $installationManager;
     private $hash;
+    private $contentHash;
     private $loader;
     private $dumper;
     private $process;
@@ -44,17 +45,18 @@ class Locker
      * Initializes packages locker.
      *
      * @param IOInterface         $io
-     * @param JsonFile            $lockFile            lockfile loader
-     * @param RepositoryManager   $repositoryManager   repository manager instance
-     * @param InstallationManager $installationManager installation manager instance
-     * @param string              $hash                unique hash of the current composer configuration
+     * @param JsonFile            $lockFile             lockfile loader
+     * @param RepositoryManager   $repositoryManager    repository manager instance
+     * @param InstallationManager $installationManager  installation manager instance
+     * @param string              $composerFileContents The contents of the composer file
      */
-    public function __construct(IOInterface $io, JsonFile $lockFile, RepositoryManager $repositoryManager, InstallationManager $installationManager, $hash)
+    public function __construct(IOInterface $io, JsonFile $lockFile, RepositoryManager $repositoryManager, InstallationManager $installationManager, $composerFileContents)
     {
         $this->lockFile = $lockFile;
         $this->repositoryManager = $repositoryManager;
         $this->installationManager = $installationManager;
-        $this->hash = $hash;
+        $this->hash = md5($composerFileContents);
+        $this->contentHash = $this->getContentHash($composerFileContents);
         $this->loader = new ArrayLoader(null, true);
         $this->dumper = new ArrayDumper();
         $this->process = new ProcessExecutor($io);
@@ -85,6 +87,11 @@ class Locker
     {
         $lock = $this->lockFile->read();
 
+        if (!empty($lock['content-hash'])) {
+            // There is a content hash key, use that instead of the file hash
+            return $this->contentHash === $lock['content-hash'];
+        }
+
         return $this->hash === $lock['hash'];
     }
 
@@ -133,11 +140,10 @@ class Locker
     public function getPlatformRequirements($withDevReqs = false)
     {
         $lockData = $this->getLockData();
-        $versionParser = new VersionParser();
         $requirements = array();
 
         if (!empty($lockData['platform'])) {
-            $requirements = $versionParser->parseLinks(
+            $requirements = $this->loader->parseLinks(
                 '__ROOT__',
                 '1.0.0',
                 'requires',
@@ -146,7 +152,7 @@ class Locker
         }
 
         if ($withDevReqs && !empty($lockData['platform-dev'])) {
-            $devRequirements = $versionParser->parseLinks(
+            $devRequirements = $this->loader->parseLinks(
                 '__ROOT__',
                 '1.0.0',
                 'requires',
@@ -239,8 +245,9 @@ class Locker
         $lock = array(
             '_readme' => array('This file locks the dependencies of your project to a known state',
                                'Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file',
-                               'This file is @gener'.'ated automatically'),
+                               'This file is @gener'.'ated automatically', ),
             'hash' => $this->hash,
+            'content-hash' => $this->contentHash,
             'packages' => null,
             'packages-dev' => null,
             'aliases' => array(),
@@ -280,7 +287,12 @@ class Locker
             return false;
         }
 
-        if (!$this->isLocked() || $lock !== $this->getLockData()) {
+        try {
+            $isLocked = $this->isLocked();
+        } catch (ParsingException $e) {
+            $isLocked = false;
+        }
+        if (!$isLocked || $lock !== $this->getLockData()) {
             $this->lockFile->write($lock);
             $this->lockDataCache = null;
 
@@ -378,4 +390,43 @@ class Locker
 
         return $datetime ? $datetime->format('Y-m-d H:i:s') : null;
     }
+
+    /**
+     * Returns the md5 hash of the sorted content of the composer file.
+     *
+     * @param string $composerFileContents The contents of the composer file.
+     *
+     * @return string
+     */
+    private function getContentHash($composerFileContents)
+    {
+        $content = json_decode($composerFileContents, true);
+
+        $relevantKeys = array(
+            'name',
+            'version',
+            'require',
+            'require-dev',
+            'conflict',
+            'replace',
+            'provide',
+            'minimum-stability',
+            'prefer-stable',
+            'repositories',
+            'extra',
+        );
+
+        $relevantContent = array();
+
+        foreach (array_intersect($relevantKeys, array_keys($content)) as $key) {
+            $relevantContent[$key] = $content[$key];
+        }
+        if (isset($content['config']['platform'])) {
+            $relevantContent['config']['platform'] = $content['config']['platform'];
+        }
+
+        ksort($relevantContent);
+
+        return md5(json_encode($relevantContent));
+    }
 }

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů