1
0
Fork 0
Conflicts:
	src/Composer/Factory.php
pull/1407/head
Nicolas Toniazzi 2014-12-15 14:36:43 +01:00
commit ad9c3d3b30
65 changed files with 1060 additions and 236 deletions

View File

@ -1,3 +1,33 @@
### 1.0.0-alpha9 (2014-12-07)
* Added `remove` command to do the reverse of `require`
* Added --ignore-platform-reqs to `install`/`update` commands to install even if you are missing a php extension or have an invalid php version
* Added a warning when abandoned packages are being installed
* Added auto-selection of the version constraint in the `require` command, which can now be used simply as `composer require foo/bar`
* Added ability to define custom composer commands using scripts
* Added `browse` command to open a browser to the given package's repo URL (or homepage with `-H`)
* Added an `autoload-dev` section to declare dev-only autoload rules + a --no-dev flag to dump-autoload
* Added an `auth.json` file, with `store-auths` config option
* Added a `http-basic` config option to store login/pwds to hosts
* Added failover to source/dist and vice-versa in case a download method fails
* Added --path (-P) flag to the show command to see the install path of packages
* Added --update-with-dependencies and --update-no-dev flags to the require command
* Added `optimize-autoloader` config option to force the `-o` flag from the config
* Added `clear-cache` command
* Added a GzipDownloader to download single gzipped files
* Added `ssh` support in the `github-protocols` config option
* Added `pre-dependencies-solving` and `post-dependencies-solving` events
* Added `pre-archive-cmd` and `post-archive-cmd` script events to the `archive` command
* Added a `no-api` flag to GitHub VCS repos to skip the API but still get zip downloads
* Added http-basic auth support for private git repos not on github
* Added support for autoloading `.hh` files when running HHVM
* Added support for PHP 5.6
* Added support for OTP auth when retrieving a GitHub API key
* Fixed isolation of `files` autoloaded scripts to ensure they can not affect anything
* Improved performance of solving dependencies
* Improved SVN and Perforce support
* A boatload of minor fixes, documentation additions and UX improvements
### 1.0.0-alpha8 (2014-01-06) ### 1.0.0-alpha8 (2014-01-06)
* Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements * Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements

134
composer.lock generated
View File

@ -120,17 +120,17 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v2.6.0", "version": "v2.6.1",
"target-dir": "Symfony/Component/Console", "target-dir": "Symfony/Component/Console",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Console.git", "url": "https://github.com/symfony/Console.git",
"reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10" "reference": "ef825fd9f809d275926547c9e57cbf14968793e8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/d3bac228fd7a2aac9193e241b239880b3ba39a10", "url": "https://api.github.com/repos/symfony/Console/zipball/ef825fd9f809d275926547c9e57cbf14968793e8",
"reference": "d3bac228fd7a2aac9193e241b239880b3ba39a10", "reference": "ef825fd9f809d275926547c9e57cbf14968793e8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -173,21 +173,21 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-11-20 13:24:23" "time": "2014-12-02 20:19:20"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v2.6.0", "version": "v2.6.1",
"target-dir": "Symfony/Component/Finder", "target-dir": "Symfony/Component/Finder",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Finder.git", "url": "https://github.com/symfony/Finder.git",
"reference": "d574347c652a14cfee0349f744c7880e1d9029fd" "reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/d574347c652a14cfee0349f744c7880e1d9029fd", "url": "https://api.github.com/repos/symfony/Finder/zipball/0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721",
"reference": "d574347c652a14cfee0349f744c7880e1d9029fd", "reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -220,21 +220,21 @@
], ],
"description": "Symfony Finder Component", "description": "Symfony Finder Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-11-28 10:00:40" "time": "2014-12-02 20:19:20"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v2.6.0", "version": "v2.6.1",
"target-dir": "Symfony/Component/Process", "target-dir": "Symfony/Component/Process",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Process.git", "url": "https://github.com/symfony/Process.git",
"reference": "dc88f75d1c07791e5733f90be747961dce26cf05" "reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/dc88f75d1c07791e5733f90be747961dce26cf05", "url": "https://api.github.com/repos/symfony/Process/zipball/bf0c9bd625f13b0b0bbe39919225cf145dfb935a",
"reference": "dc88f75d1c07791e5733f90be747961dce26cf05", "reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -267,7 +267,7 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-11-04 14:29:39" "time": "2014-12-02 20:19:20"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -327,16 +327,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "2.0.12", "version": "2.0.13",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7ce9da20f96964bb7a4033f53834df13328dbeab" "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ce9da20f96964bb7a4033f53834df13328dbeab", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5",
"reference": "7ce9da20f96964bb7a4033f53834df13328dbeab", "reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -388,7 +388,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2014-12-02 13:17:01" "time": "2014-12-03 06:41:44"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -574,16 +574,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "4.3.5", "version": "4.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1" "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0",
"reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1", "reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -600,8 +600,9 @@
"phpunit/phpunit-mock-objects": "~2.3", "phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.0", "sebastian/comparator": "~1.0",
"sebastian/diff": "~1.1", "sebastian/diff": "~1.1",
"sebastian/environment": "~1.0", "sebastian/environment": "~1.1",
"sebastian/exporter": "~1.0", "sebastian/exporter": "~1.0",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0", "sebastian/version": "~1.0",
"symfony/yaml": "~2.0" "symfony/yaml": "~2.0"
}, },
@ -614,7 +615,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.3.x-dev" "dev-master": "4.4.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -623,10 +624,6 @@
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"include-path": [
"",
"../../symfony/yaml/"
],
"license": [ "license": [
"BSD-3-Clause" "BSD-3-Clause"
], ],
@ -638,13 +635,13 @@
} }
], ],
"description": "The PHP Unit Testing framework.", "description": "The PHP Unit Testing framework.",
"homepage": "http://www.phpunit.de/", "homepage": "https://phpunit.de/",
"keywords": [ "keywords": [
"phpunit", "phpunit",
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2014-11-11 10:11:09" "time": "2014-12-05 06:49:03"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -703,16 +700,16 @@
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
"version": "1.0.1", "version": "1.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git", "url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef" "reference": "c484a80f97573ab934e37826dba0135a3301b26a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a",
"reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", "reference": "c484a80f97573ab934e37826dba0135a3301b26a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -726,7 +723,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.0.x-dev" "dev-master": "1.1.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -763,7 +760,7 @@
"compare", "compare",
"equality" "equality"
], ],
"time": "2014-05-11 23:00:21" "time": "2014-11-16 21:32:38"
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
@ -932,6 +929,57 @@
], ],
"time": "2014-09-10 00:51:36" "time": "2014-09-10 00:51:36"
}, },
{
"name": "sebastian/global-state",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"time": "2014-10-06 09:23:50"
},
{ {
"name": "sebastian/version", "name": "sebastian/version",
"version": "1.0.3", "version": "1.0.3",
@ -969,17 +1017,17 @@
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v2.6.0", "version": "v2.6.1",
"target-dir": "Symfony/Component/Yaml", "target-dir": "Symfony/Component/Yaml",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Yaml.git", "url": "https://github.com/symfony/Yaml.git",
"reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8" "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", "url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20",
"reference": "51c845cf3e4bfc182d1d5c05ed1c7338361d86f8", "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1012,7 +1060,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-11-20 13:24:23" "time": "2014-12-02 20:19:20"
} }
], ],
"aliases": [], "aliases": [],

View File

@ -47,7 +47,7 @@ any version beginning with `1.2`.
## System Requirements ## System Requirements
Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile
flags are also required, but the installer will warn you about any flags are also required, but when using the installer you will be warned about any
incompatibilities. incompatibilities.
To install packages from sources instead of simple zip archives, you will need To install packages from sources instead of simple zip archives, you will need
@ -56,14 +56,17 @@ git, svn or hg depending on how the package is version-controlled.
Composer is multi-platform and we strive to make it run equally well on Windows, Composer is multi-platform and we strive to make it run equally well on Windows,
Linux and OSX. Linux and OSX.
## Installation - *nix ## Installation - Linux / Unix / OSX
### Downloading the Composer Executable ### Downloading the Composer Executable
There are in short, two ways to install Composer. Locally as part of your
project, or globally as a system wide executable.
#### Locally #### Locally
To actually get Composer, we need to do two things. The first one is installing Installing Composer locally is a matter of just running the installer in your
Composer (again, this means downloading it into your project): project directory:
```sh ```sh
curl -sS https://getcomposer.org/installer | php curl -sS https://getcomposer.org/installer | php
@ -76,8 +79,8 @@ curl -sS https://getcomposer.org/installer | php
php -r "readfile('https://getcomposer.org/installer');" | php php -r "readfile('https://getcomposer.org/installer');" | php
``` ```
This will just check a few PHP settings and then download `composer.phar` to The installer will just check a few PHP settings and then download `composer.phar`
your working directory. This file is the Composer binary. It is a PHAR (PHP 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 archive), which is an archive format for PHP which can be run on the command
line, amongst other things. line, amongst other things.
@ -106,17 +109,6 @@ mv composer.phar /usr/local/bin/composer
Then, just run `composer` in order to run Composer instead of `php composer.phar`. Then, just run `composer` in order to run Composer instead of `php composer.phar`.
#### Globally (on OSX via homebrew)
Composer is part of the homebrew-php project.
```sh
brew update
brew tap homebrew/dupes
brew tap homebrew/php
brew install composer
```
## Installation - Windows ## Installation - Windows
### Using the Installer ### Using the Installer
@ -127,6 +119,9 @@ Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe
it will install the latest Composer version and set up your PATH so that you can 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. 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.
### Manual Installation ### Manual Installation
Change to a directory on your `PATH` and run the install snippet to download Change to a directory on your `PATH` and run the install snippet to download

View File

@ -1,29 +1,8 @@
# Basic usage # Basic usage
## Installation ## Installing
To install Composer, you just need to download the `composer.phar` executable. If you have not yet installed Composer, refer to to the [Intro](00-intro.md) chapter.
```sh
curl -sS https://getcomposer.org/installer | php
```
For the details, see the [Introduction](00-intro.md) chapter.
To check if Composer is working, just run the PHAR through `php`:
```sh
php composer.phar
```
This should give you a list of available commands.
> **Note:** You can also perform the checks only without downloading Composer
> by using the `--check` option. For more information, just use `--help`.
>
> ```sh
> curl -sS https://getcomposer.org/installer | php -- --help
> ```
## `composer.json`: Project Setup ## `composer.json`: Project Setup
@ -73,16 +52,18 @@ means any version in the `1.0` development branch. It would match `1.0.0`,
Version constraints can be specified in a few different ways. Version constraints can be specified in a few different ways.
Name | Example | Description Name | Example | Description
-------------- | ------------------------------------------------------------------ | ----------- -------------- | ------------------------------------------------------------------------ | -----------
Exact version | `1.0.2` | You can specify the exact version of a package. 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; &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 comma (`,`) will be treated as a **logical AND**. A pipe (<code>&#124;</code>) will be treated as a **logical OR**. AND has higher precedence than OR. 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.
Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`. 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`.
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. Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`.
Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2 <2.0`. For more details, read the next section below.
Caret Operator | `^1.2.3` | Very useful for projects that follow semantic versioning. `^1.2.3` is equivalent to `>=1.2.3 <2.0`. For more details, read the next section below.
### Next Significant Release (Tilde Operator) ### Next Significant Release (Tilde and Caret Operators)
The `~` operator is best explained by example: `~1.2` is equivalent to The `~` operator is best explained by example: `~1.2` is equivalent to
`>=1.2,<2.0`, while `~1.2.3` is equivalent to `>=1.2.3,<1.3`. As you can see `>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
it is mostly useful for projects respecting [semantic it is mostly useful for projects respecting [semantic
versioning](http://semver.org/). A common usage would be to mark the minimum 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 minor version you depend on, like `~1.2` (which allows anything up to, but not
@ -90,6 +71,12 @@ including, 2.0). Since in theory there should be no backwards compatibility
breaks until 2.0, that works well. Another way of looking at it is that using 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. `~` 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 > **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` > like `~1.2` would not install it. As said above `~1.2` only means the `.2`
> can change but the `1.` part is fixed. > can change but the `1.` part is fixed.

View File

@ -140,7 +140,9 @@ php composer.phar update vendor/*
* **--lock:** Only updates the lock file hash to suppress warning about the * **--lock:** Only updates the lock file hash to suppress warning about the
lock file being out of date. 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.
So all packages with their dependencies are updated recursively. * **--prefer-stable:** Prefer stable versions of dependencies.
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
versions of requirements, generally used with `--prefer-stable`.
## require ## require
@ -482,11 +484,20 @@ performance.
a bit of time to run so it is currently not done by default. a bit of time to run so it is currently not done by default.
* **--no-dev:** Disables autoload-dev rules. * **--no-dev:** Disables autoload-dev rules.
## clear-cache
Deletes all content from Composer's cache directories.
## licenses ## licenses
Lists the name, version and license of every package installed. Use Lists the name, version and license of every package installed. Use
`--format=json` to get machine readable output. `--format=json` to get machine readable output.
### Options
* **--no-dev:** Remove dev dependencies from the output
* **--format:** Format of the output: text or json (default: "text")
## run-script ## run-script
To run [scripts](articles/scripts.md) manually you can use this command, To run [scripts](articles/scripts.md) manually you can use this command,

View File

@ -345,7 +345,7 @@ dependencies from being installed.
Lists packages that conflict with this version of this package. They Lists packages that conflict with this version of this package. They
will not be allowed to be installed together with your package. will not be allowed to be installed together with your package.
Note that when specifying ranges like `<1.0, >= 1.1` in a `conflict` link, Note that when specifying ranges like `<1.0 >=1.1` in a `conflict` link,
this will state a conflict with all versions that are less than 1.0 *and* equal this will state a conflict with all versions that are less than 1.0 *and* equal
or newer than 1.1 at the same time, which is probably not what you want. You or newer than 1.1 at the same time, which is probably not what you want. You
probably want to go for `<1.0 | >=1.1` in this case. probably want to go for `<1.0 | >=1.1` in this case.

View File

@ -0,0 +1,59 @@
<!--
tagline: Access privately hosted packages
-->
# HTTP basic authentication
Your [Satis or Toran Proxy](handling-private-packages-with-satis.md) server
could be secured with http basic authentication. In order to allow your project
to have access to these packages you will have to tell composer how to
authenticate with your credentials.
The simplest way to provide your credentials is providing your set
of credentials inline with the repository specification such as:
```json
{
"repositories": [
{
"type": "composer",
"url": "http://extremely:secret@repo.example.org"
}
]
}
```
This will basically teach composer how to authenticate automatically
when reading packages from the provided composer repository.
This does not work for everybody especially when you don't want to
hard code your credentials into your composer.json. There is a second
way to provide these details and it is via interaction. If you don't
provide the authentication credentials composer will prompt you upon
connection to enter the username and password.
The third way if you want to pre-configure it is via an `auth.json` file
located in your `COMPOSER_HOME` or besides your `composer.json`.
The file should contain a set of hostnames followed each with their own
username/password pairs, for example:
```json
{
"basic-auth": [
"repo.example1.org": {
"username": "my-username1",
"password": "my-secret-password1"
},
"repo.example2.org": {
"username": "my-username2",
"password": "my-secret-password2"
}
]
}
```
The main advantage of the auth.json file is that it can be gitignored so
that every developer in your team can place their own credentials in there,
which makes revokation of credentials much easier than if you all share the
same.

View File

@ -59,6 +59,7 @@ class ClassLoader
if (!empty($this->prefixesPsr0)) { if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0); return call_user_func_array('array_merge', $this->prefixesPsr0);
} }
return array(); return array();
} }

View File

@ -68,6 +68,15 @@ abstract class Command extends BaseCommand
$this->composer = $composer; $this->composer = $composer;
} }
/**
* Removes the cached composer instance
*/
public function resetComposer()
{
$this->composer = null;
$this->getApplication()->resetComposer();
}
/** /**
* @return IOInterface * @return IOInterface
*/ */

View File

@ -234,8 +234,14 @@ EOT
{ {
if (null === $repositoryUrl) { if (null === $repositoryUrl) {
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION) && file_exists($repositoryUrl)) {
$sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config))); $json = new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config));
$data = $json->read();
if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) {
$sourceRepo = new ComposerRepository(array('url' => 'file://' . strtr(realpath($repositoryUrl), '\\', '/')), $io, $config);
} else {
$sourceRepo = new FilesystemRepository($json);
}
} elseif (0 === strpos($repositoryUrl, 'http')) { } elseif (0 === strpos($repositoryUrl, 'http')) {
$sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config);
} else { } else {

View File

@ -255,7 +255,7 @@ EOT
$latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false)); $latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') { if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') {
return '<warning>Your are not running the latest version</warning>'; return '<warning>You are not running the latest version</warning>';
} }
return true; return true;

View File

@ -33,8 +33,9 @@ use Symfony\Component\Process\ExecutableFinder;
*/ */
class InitCommand extends Command class InitCommand extends Command
{ {
protected $repos;
private $gitConfig; private $gitConfig;
private $repos;
private $pool; private $pool;
public function parseAuthorString($author) public function parseAuthorString($author)

View File

@ -16,6 +16,8 @@ use Composer\Json\JsonFile;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Plugin\CommandEvent; use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents; use Composer\Plugin\PluginEvents;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
use Symfony\Component\Console\Helper\TableHelper; use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -33,6 +35,7 @@ class LicensesCommand extends Command
->setDescription('Show information about licenses of dependencies') ->setDescription('Show information about licenses of dependencies')
->setDefinition(array( ->setDefinition(array(
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The license command displays detailed information about the licenses of The license command displays detailed information about the licenses of
@ -55,9 +58,10 @@ EOT
$versionParser = new VersionParser; $versionParser = new VersionParser;
$packages = array(); if ($input->getOption('no-dev')) {
foreach ($repo->getPackages() as $package) { $packages = $this->filterRequiredPackages($repo, $root);
$packages[$package->getName()] = $package; } else {
$packages = $this->appendPackages($repo->getPackages(), array());
} }
ksort($packages); ksort($packages);
@ -102,4 +106,47 @@ EOT
throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format)); throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format));
} }
} }
/**
* Find package requires and child requires
*
* @param RepositoryInterface $repo
* @param PackageInterface $package
*/
private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array())
{
$requires = array_keys($package->getRequires());
$packageListNames = array_keys($bucket);
$packages = array_filter(
$repo->getPackages(),
function ($package) use ($requires, $packageListNames) {
return in_array($package->getName(), $requires) && !in_array($package->getName(), $packageListNames);
}
);
$bucket = $this->appendPackages($packages, $bucket);
foreach ($packages as $package) {
$bucket = $this->filterRequiredPackages($repo, $package, $bucket);
}
return $bucket;
}
/**
* Adds packages to the package list
*
* @param array $packages the list of packages to add
* @param array $bucket the list to add packages to
* @return array
*/
public function appendPackages(array $packages, array $bucket)
{
foreach ($packages as $package) {
$bucket[$package->getName()] = $package;
}
return $bucket;
}
} }

View File

@ -23,6 +23,8 @@ use Composer\Json\JsonManipulator;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Plugin\CommandEvent; use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents; use Composer\Plugin\PluginEvents;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
/** /**
* @author Jérémy Romey <jeremy@free-agent.fr> * @author Jérémy Romey <jeremy@free-agent.fr>
@ -45,6 +47,7 @@ class RequireCommand extends InitCommand
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-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('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('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The require command adds required packages to your composer.json and installs them The require command adds required packages to your composer.json and installs them
@ -78,14 +81,22 @@ EOT
} }
$json = new JsonFile($file); $json = new JsonFile($file);
$composer = $json->read(); $composerDefinition = $json->read();
$composerBackup = file_get_contents($json->getPath()); $composerBackup = file_get_contents($json->getPath());
$composer = $this->getComposer();
$repos = $composer->getRepositoryManager()->getRepositories();
$this->repos = new CompositeRepository(array_merge(
array(new PlatformRepository),
$repos
));
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'));
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
$baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array();
$requirements = $this->formatRequirements($requirements); $requirements = $this->formatRequirements($requirements);
// validate requirements format // validate requirements format
@ -94,17 +105,19 @@ EOT
$versionParser->parseConstraints($constraint); $versionParser->parseConstraints($constraint);
} }
if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey)) { $sortPackages = $input->getOption('sort-packages');
if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) {
foreach ($requirements as $package => $version) { foreach ($requirements as $package => $version) {
$baseRequirements[$package] = $version; $baseRequirements[$package] = $version;
if (isset($composer[$removeKey][$package])) { if (isset($composerDefinition[$removeKey][$package])) {
unset($composer[$removeKey][$package]); unset($composerDefinition[$removeKey][$package]);
} }
} }
$composer[$requireKey] = $baseRequirements; $composerDefinition[$requireKey] = $baseRequirements;
$json->write($composer); $json->write($composerDefinition);
} }
$output->writeln('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>'); $output->writeln('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
@ -115,6 +128,7 @@ EOT
$updateDevMode = !$input->getOption('update-no-dev'); $updateDevMode = !$input->getOption('update-no-dev');
// Update packages // Update packages
$this->resetComposer();
$composer = $this->getComposer(); $composer = $this->getComposer();
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$io = $this->getIO(); $io = $this->getIO();
@ -149,14 +163,14 @@ EOT
return $status; return $status;
} }
private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey) private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey, $sortPackages)
{ {
$contents = file_get_contents($json->getPath()); $contents = file_get_contents($json->getPath());
$manipulator = new JsonManipulator($contents); $manipulator = new JsonManipulator($contents);
foreach ($new as $package => $constraint) { foreach ($new as $package => $constraint) {
if (!$manipulator->addLink($requireKey, $package, $constraint)) { if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) {
return false; return false;
} }
if (!$manipulator->removeSubNode($removeKey, $package)) { if (!$manipulator->removeSubNode($removeKey, $package)) {

View File

@ -43,6 +43,7 @@ class SelfUpdateCommand extends Command
new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'), new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'),
new InputOption('clean-backups', null, InputOption::VALUE_NONE, 'Delete old backups during an update. This makes the current version of composer the only backup available after the update'), new InputOption('clean-backups', null, InputOption::VALUE_NONE, 'Delete old backups during an update. This makes the current version of composer the only backup available after the update'),
new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'), new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The <info>self-update</info> command checks getcomposer.org for newer The <info>self-update</info> command checks getcomposer.org for newer
@ -105,7 +106,7 @@ EOT
$output->writeln(sprintf("Updating to version <info>%s</info>.", $updateVersion)); $output->writeln(sprintf("Updating to version <info>%s</info>.", $updateVersion));
$remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar"); $remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar");
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename); $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
if (!file_exists($tempFilename)) { if (!file_exists($tempFilename)) {
$output->writeln('<error>The download of the new composer version failed for an unexpected reason</error>'); $output->writeln('<error>The download of the new composer version failed for an unexpected reason</error>');

View File

@ -47,6 +47,8 @@ class UpdateCommand extends Command
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), 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.'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The <info>update</info> command reads the composer.json file from the The <info>update</info> command reads the composer.json file from the
@ -121,6 +123,8 @@ EOT
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
->setWhitelistDependencies($input->getOption('with-dependencies')) ->setWhitelistDependencies($input->getOption('with-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
; ;
if ($input->getOption('no-plugins')) { if ($input->getOption('no-plugins')) {

View File

@ -55,7 +55,7 @@ class Compiler
$date->setTimezone(new \DateTimeZone('UTC')); $date->setTimezone(new \DateTimeZone('UTC'));
$this->versionDate = $date->format('Y-m-d H:i:s'); $this->versionDate = $date->format('Y-m-d H:i:s');
$process = new Process('git describe --tags HEAD'); $process = new Process('git describe --tags --exact-match HEAD');
if ($process->run() == 0) { if ($process->run() == 0) {
$this->version = trim($process->getOutput()); $this->version = trim($process->getOutput());
} else { } else {
@ -212,6 +212,16 @@ class Compiler
* the license that is located at the bottom of this file. * the license that is located at the bottom of this file.
*/ */
// Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264
if (extension_loaded('apc') && ini_get('apc.enable_cli') && ini_get('apc.cache_by_default')) {
if (version_compare(phpversion('apc'), '3.0.12', '>=')) {
ini_set('apc.cache_by_default', 0);
} else {
fwrite(STDERR, 'Warning: APC <= 3.0.12 may cause fatal errors when running composer commands.'.PHP_EOL);
fwrite(STDERR, 'Update APC, or set apc.enable_cli or apc.cache_by_default to 0 in your php.ini.'.PHP_EOL);
}
}
Phar::mapPhar('composer.phar'); Phar::mapPhar('composer.phar');
EOF; EOF;

View File

@ -99,7 +99,8 @@ class Application extends BaseApplication
if ($name = $this->getCommandName($input)) { if ($name = $this->getCommandName($input)) {
try { try {
$commandName = $this->find($name)->getName(); $commandName = $this->find($name)->getName();
} catch (\InvalidArgumentException $e) {} } catch (\InvalidArgumentException $e) {
}
} }
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
if (time() > COMPOSER_DEV_WARNING_TIME) { if (time() > COMPOSER_DEV_WARNING_TIME) {
@ -176,7 +177,7 @@ class Application extends BaseApplication
public function renderException($exception, $output) public function renderException($exception, $output)
{ {
try { try {
$composer = $this->getComposer(false); $composer = $this->getComposer(false, true);
if ($composer) { if ($composer) {
$config = $composer->getConfig(); $config = $composer->getConfig();
@ -190,6 +191,16 @@ class Application extends BaseApplication
} catch (\Exception $e) { } catch (\Exception $e) {
} }
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
$output->writeln('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
$output->writeln('<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')) {
$output->writeln('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
$output->writeln('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
}
return parent::renderException($exception, $output); return parent::renderException($exception, $output);
} }
@ -219,6 +230,14 @@ class Application extends BaseApplication
return $this->composer; return $this->composer;
} }
/**
* Removes the cached composer instance
*/
public function resetComposer()
{
$this->composer = null;
}
/** /**
* @return IOInterface * @return IOInterface
*/ */

View File

@ -24,10 +24,12 @@ use Composer\Package\LinkConstraint\VersionConstraint;
class DefaultPolicy implements PolicyInterface class DefaultPolicy implements PolicyInterface
{ {
private $preferStable; private $preferStable;
private $preferLowest;
public function __construct($preferStable = false) public function __construct($preferStable = false, $preferLowest = false)
{ {
$this->preferStable = $preferStable; $this->preferStable = $preferStable;
$this->preferLowest = $preferLowest;
} }
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator) public function versionCompare(PackageInterface $a, PackageInterface $b, $operator)
@ -195,6 +197,7 @@ class DefaultPolicy implements PolicyInterface
protected function pruneToBestVersion(Pool $pool, $literals) protected function pruneToBestVersion(Pool $pool, $literals)
{ {
$operator = $this->preferLowest ? '<' : '>';
$bestLiterals = array($literals[0]); $bestLiterals = array($literals[0]);
$bestPackage = $pool->literalToPackage($literals[0]); $bestPackage = $pool->literalToPackage($literals[0]);
foreach ($literals as $i => $literal) { foreach ($literals as $i => $literal) {
@ -204,7 +207,7 @@ class DefaultPolicy implements PolicyInterface
$package = $pool->literalToPackage($literal); $package = $pool->literalToPackage($literal);
if ($this->versionCompare($package, $bestPackage, '>')) { if ($this->versionCompare($package, $bestPackage, $operator)) {
$bestPackage = $package; $bestPackage = $package;
$bestLiterals = array($literal); $bestLiterals = array($literal);
} elseif ($this->versionCompare($package, $bestPackage, '==')) { } elseif ($this->versionCompare($package, $bestPackage, '==')) {

View File

@ -241,7 +241,7 @@ class RuleSetGenerator
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package)); $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
} elseif (!$this->obsoleteImpossibleForAlias($package, $provider) && $package->id <= $provider->id) { } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) {
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, $package)); $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, $package));
} }

View File

@ -155,6 +155,7 @@ class EventDispatcher
$return = 0; $return = 0;
foreach ($listeners as $callable) { foreach ($listeners as $callable) {
if (!is_string($callable) && is_callable($callable)) { if (!is_string($callable) && is_callable($callable)) {
$event = $this->checkListenerExpectedEvent($callable, $event);
$return = false === call_user_func($callable, $event) ? 1 : 0; $return = false === call_user_func($callable, $event) ? 1 : 0;
} elseif ($this->isPhpScript($callable)) { } elseif ($this->isPhpScript($callable)) {
$className = substr($callable, 0, strpos($callable, '::')); $className = substr($callable, 0, strpos($callable, '::'));
@ -200,9 +201,43 @@ class EventDispatcher
*/ */
protected function executeEventPhpScript($className, $methodName, Event $event) protected function executeEventPhpScript($className, $methodName, Event $event)
{ {
$event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
return $className::$methodName($event); return $className::$methodName($event);
} }
/**
* @param mixed $target
* @param Event $event
* @return Event|CommandEvent
*/
protected function checkListenerExpectedEvent($target, Event $event)
{
if (!$event instanceof Script\Event) {
return $event;
}
try {
$reflected = new \ReflectionParameter($target, 0);
} catch (\Exception $e) {
return $event;
}
$typehint = $reflected->getClass();
if (!$typehint instanceof \ReflectionClass) {
return $event;
}
$expected = $typehint->getName();
if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') {
$event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments());
}
return $event;
}
/** /**
* Add a listener for a particular event * Add a listener for a particular event
* *

View File

@ -107,6 +107,8 @@ class Installer
protected $update = false; protected $update = false;
protected $runScripts = true; protected $runScripts = true;
protected $ignorePlatformReqs = false; protected $ignorePlatformReqs = false;
protected $preferStable = false;
protected $preferLowest = false;
/** /**
* Array of package names/globs flagged for update * Array of package names/globs flagged for update
* *
@ -307,7 +309,8 @@ class Installer
$aliases, $aliases,
$this->package->getMinimumStability(), $this->package->getMinimumStability(),
$this->package->getStabilityFlags(), $this->package->getStabilityFlags(),
$this->package->getPreferStable() $this->preferStable || $this->package->getPreferStable(),
$this->preferLowest
); );
if ($updatedLock) { if ($updatedLock) {
$this->io->write('<info>Writing lock file</info>'); $this->io->write('<info>Writing lock file</info>');
@ -694,16 +697,21 @@ class Installer
private function createPolicy() private function createPolicy()
{ {
$preferStable = null; $preferStable = null;
$preferLowest = null;
if (!$this->update && $this->locker->isLocked()) { if (!$this->update && $this->locker->isLocked()) {
$preferStable = $this->locker->getPreferStable(); $preferStable = $this->locker->getPreferStable();
$preferLowest = $this->locker->getPreferLowest();
} }
// old lock file without prefer stable will return null // old lock file without prefer stable/lowest will return null
// so in this case we use the composer.json info // so in this case we use the composer.json info
if (null === $preferStable) { if (null === $preferStable) {
$preferStable = $this->package->getPreferStable(); $preferStable = $this->preferStable || $this->package->getPreferStable();
}
if (null === $preferLowest) {
$preferLowest = $this->preferLowest;
} }
return new DefaultPolicy($preferStable); return new DefaultPolicy($preferStable, $preferLowest);
} }
private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo) private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo)
@ -1244,6 +1252,32 @@ class Installer
return $this; return $this;
} }
/**
* Should packages be prefered in a stable version when updating?
*
* @param boolean $preferStable
* @return Installer
*/
public function setPreferStable($preferStable = true)
{
$this->preferStable = (boolean) $preferStable;
return $this;
}
/**
* Should packages be prefered in a lowest version when updating?
*
* @param boolean $preferLowest
* @return Installer
*/
public function setPreferLowest($preferLowest = true)
{
$this->preferLowest = (boolean) $preferLowest;
return $this;
}
/** /**
* Disables plugins. * Disables plugins.
* *

View File

@ -31,7 +31,7 @@ class JsonManipulator
if (!self::$RECURSE_BLOCKS) { if (!self::$RECURSE_BLOCKS) {
self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*'; self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*'; self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*';
self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])+"'; self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"';
self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})'; self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})';
} }
@ -52,7 +52,7 @@ class JsonManipulator
return $this->contents . $this->newline; return $this->contents . $this->newline;
} }
public function addLink($type, $package, $constraint) public function addLink($type, $package, $constraint, $sortPackages = false)
{ {
$decoded = JsonFile::parseJson($this->contents); $decoded = JsonFile::parseJson($this->contents);
@ -90,6 +90,13 @@ class JsonManipulator
} }
} }
if (true === $sortPackages) {
$requirements = json_decode($links, true);
ksort($requirements);
$links = $this->format($requirements);
}
$this->contents = $matches[1] . $matches[2] . $links . $matches[4]; $this->contents = $matches[1] . $matches[2] . $links . $matches[4];
return true; return true;

View File

@ -137,7 +137,7 @@ class ArchiveManager
$sourcePath = realpath('.'); $sourcePath = realpath('.');
} else { } else {
// Directory used to download the sources // Directory used to download the sources
$sourcePath = sys_get_temp_dir().'/composer_archiver/arch'.uniqid(); $sourcePath = sys_get_temp_dir().'/composer_archive'.uniqid();
$filesystem->ensureDirectoryExists($sourcePath); $filesystem->ensureDirectoryExists($sourcePath);
// Download sources // Download sources
@ -154,7 +154,7 @@ class ArchiveManager
} }
// Create the archive // Create the archive
$tempTarget = sys_get_temp_dir().'/composer_archiver/arch'.uniqid().'.'.$format; $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format;
$filesystem->ensureDirectoryExists(dirname($tempTarget)); $filesystem->ensureDirectoryExists(dirname($tempTarget));
$archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes()); $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes());
@ -164,6 +164,7 @@ class ArchiveManager
if (!$package instanceof RootPackageInterface) { if (!$package instanceof RootPackageInterface) {
$filesystem->removeDirectory($sourcePath); $filesystem->removeDirectory($sourcePath);
} }
$filesystem->remove($tempTarget);
return $target; return $target;
} }

View File

@ -56,7 +56,6 @@ abstract class BasePackage implements PackageInterface
protected $repository; protected $repository;
protected $transportOptions; protected $transportOptions;
/** /**
* All descendants' constructors should call this parent constructor * All descendants' constructors should call this parent constructor
* *

View File

@ -182,6 +182,15 @@ class Locker
return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null; return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null;
} }
public function getPreferLowest()
{
$lockData = $this->getLockData();
// return null if not set to allow caller logic to choose the
// right behavior since old lock files have no prefer-lowest
return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null;
}
public function getAliases() public function getAliases()
{ {
$lockData = $this->getLockData(); $lockData = $this->getLockData();
@ -213,10 +222,11 @@ class Locker
* @param string $minimumStability * @param string $minimumStability
* @param array $stabilityFlags * @param array $stabilityFlags
* @param bool $preferStable * @param bool $preferStable
* @param bool $preferLowest
* *
* @return bool * @return bool
*/ */
public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable) public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest)
{ {
$lock = array( $lock = array(
'_readme' => array('This file locks the dependencies of your project to a known state', '_readme' => array('This file locks the dependencies of your project to a known state',
@ -229,6 +239,7 @@ class Locker
'minimum-stability' => $minimumStability, 'minimum-stability' => $minimumStability,
'stability-flags' => $stabilityFlags, 'stability-flags' => $stabilityFlags,
'prefer-stable' => $preferStable, 'prefer-stable' => $preferStable,
'prefer-lowest' => $preferLowest,
); );
foreach ($aliases as $package => $versions) { foreach ($aliases as $package => $versions) {

View File

@ -103,6 +103,11 @@ class VersionParser
$version = $match[1]; $version = $match[1];
} }
// ignore build metadata
if (preg_match('{^([^,\s+]+)\+[^\s]+$}', $version, $match)) {
$version = $match[1];
}
// match master-like branches // match master-like branches
if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
return '9999999-dev'; return '9999999-dev';
@ -178,10 +183,10 @@ class VersionParser
return $this->normalize($name); return $this->normalize($name);
} }
if (preg_match('#^v?(\d+)(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?$#i', $name, $matches)) { if (preg_match('#^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$#i', $name, $matches)) {
$version = ''; $version = '';
for ($i = 1; $i < 5; $i++) { for ($i = 1; $i < 5; $i++) {
$version .= isset($matches[$i]) ? str_replace('*', 'x', $matches[$i]) : '.x'; $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
} }
return str_replace('x', '9999999', $version).'-dev'; return str_replace('x', '9999999', $version).'-dev';
@ -230,11 +235,10 @@ class VersionParser
$constraints = $match[1]; $constraints = $match[1];
} }
$orConstraints = preg_split('{\s*\|\s*}', trim($constraints)); $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
$orGroups = array(); $orGroups = array();
foreach ($orConstraints as $constraints) { foreach ($orConstraints as $constraints) {
$andConstraints = preg_split('{\s*,\s*}', $constraints); $andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
if (count($andConstraints) > 1) { if (count($andConstraints) > 1) {
$constraintObjects = array(); $constraintObjects = array();
foreach ($andConstraints as $constraint) { foreach ($andConstraints as $constraint) {
@ -273,16 +277,18 @@ class VersionParser
} }
} }
if (preg_match('{^[x*](\.[x*])*$}i', $constraint)) { if (preg_match('{^[xX*](\.[xX*])*$}i', $constraint)) {
return array(new EmptyConstraint); return array(new EmptyConstraint);
} }
$versionRegex = '(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex;
// match tilde constraints // match tilde constraints
// like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous // like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
// version, to ensure that unstable instances of the current version are allowed. // version, to ensure that unstable instances of the current version are allowed.
// however, if a stability suffix is added to the constraint, then a >= match on the current version is // however, if a stability suffix is added to the constraint, then a >= match on the current version is
// used instead // used instead
if (preg_match('{^~>?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex.'?$}i', $constraint, $matches)) { if (preg_match('{^~>?'.$versionRegex.'$}i', $constraint, $matches)) {
if (substr($constraint, 0, 2) === '~>') { if (substr($constraint, 0, 2) === '~>') {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
'Could not parse version constraint '.$constraint.': '. 'Could not parse version constraint '.$constraint.': '.
@ -329,8 +335,39 @@ class VersionParser
); );
} }
// match caret constraints
if (preg_match('{^\^'.$versionRegex.'($)}i', $constraint, $matches)) {
// Work out which position in the version we are operating at
if ('0' !== $matches[1] || '' === $matches[2]) {
$position = 1;
} elseif ('0' !== $matches[2] || '' === $matches[3]) {
$position = 2;
} else {
$position = 3;
}
// Calculate the stability suffix
$stabilitySuffix = '';
if (empty($matches[5]) && empty($matches[7])) {
$stabilitySuffix .= '-dev';
}
$lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
$lowerBound = new VersionConstraint('>=', $lowVersion);
// For upper bound, we increment the position of one more significance,
// but highPosition = 0 would be illegal
$highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
$upperBound = new VersionConstraint('<', $highVersion);
return array(
$lowerBound,
$upperBound
);
}
// match wildcard constraints // match wildcard constraints
if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$}', $constraint, $matches)) { if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[xX*]$}', $constraint, $matches)) {
if (isset($matches[3]) && '' !== $matches[3]) { if (isset($matches[3]) && '' !== $matches[3]) {
$position = 3; $position = 3;
} elseif (isset($matches[2]) && '' !== $matches[2]) { } elseif (isset($matches[2]) && '' !== $matches[2]) {
@ -352,6 +389,33 @@ class VersionParser
); );
} }
// match hyphen constraints
if (preg_match('{^(?P<from>'.$versionRegex.') +- +(?P<to>'.$versionRegex.')($)}i', $constraint, $matches)) {
// Calculate the stability suffix
$lowStabilitySuffix = '';
if (empty($matches[6]) && empty($matches[8])) {
$lowStabilitySuffix = '-dev';
}
$lowVersion = $this->normalize($matches['from']);
$lowerBound = new VersionConstraint('>=', $lowVersion . $lowStabilitySuffix);
$highVersion = $matches[10];
if ((!empty($matches[11]) && !empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) {
$highVersion = $this->normalize($matches['to']);
$upperBound = new VersionConstraint('<=', $highVersion);
} else {
$highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]);
$highVersion = $this->manipulateVersionString($highMatch, empty($matches[11]) ? 1 : 2, 1) . '-dev';
$upperBound = new VersionConstraint('<', $highVersion);
}
return array(
$lowerBound,
$upperBound
);
}
// match operators constraints // match operators constraints
if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) { if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
try { try {
@ -360,7 +424,7 @@ class VersionParser
if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') { if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') {
$version .= '-' . $stabilityModifier; $version .= '-' . $stabilityModifier;
} elseif ('<' === $matches[1]) { } elseif ('<' === $matches[1]) {
if (!preg_match('/-stable$/', strtolower($matches[2]))) { if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
$version .= '-dev'; $version .= '-dev';
} }
} }

View File

@ -103,10 +103,22 @@ class VersionSelector
// attempt to transform 2.1.1 to 2.1 // attempt to transform 2.1.1 to 2.1
// this allows you to upgrade through minor versions // this allows you to upgrade through minor versions
$semanticVersionParts = explode('.', $version); $semanticVersionParts = explode('.', $version);
$op = '~';
// check to see if we have a semver-looking version // check to see if we have a semver-looking version
if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) { if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) {
// remove the last parts (i.e. the patch version number and any extra) // remove the last parts (i.e. the patch version number and any extra)
if ($semanticVersionParts[0] === '0') {
if ($semanticVersionParts[1] === '0') {
$semanticVersionParts[3] = '*';
} else {
$semanticVersionParts[2] = '*';
unset($semanticVersionParts[3]);
}
$op = '';
} else {
unset($semanticVersionParts[2], $semanticVersionParts[3]); unset($semanticVersionParts[2], $semanticVersionParts[3]);
}
$version = implode('.', $semanticVersionParts); $version = implode('.', $semanticVersionParts);
} else { } else {
return $prettyVersion; return $prettyVersion;
@ -118,7 +130,7 @@ class VersionSelector
} }
// 2.1 -> ~2.1 // 2.1 -> ~2.1
return '~'.$version; return $op.$version;
} }
private function getParser() private function getParser()

View File

@ -28,12 +28,13 @@ use Composer\DependencyResolver\Pool;
* Plugin manager * Plugin manager
* *
* @author Nils Adermann <naderman@naderman.de> * @author Nils Adermann <naderman@naderman.de>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class PluginManager class PluginManager
{ {
protected $composer; protected $composer;
protected $io; protected $io;
protected $globalRepository; protected $globalComposer;
protected $versionParser; protected $versionParser;
protected $plugins = array(); protected $plugins = array();
@ -44,15 +45,15 @@ class PluginManager
/** /**
* Initializes plugin manager * Initializes plugin manager
* *
* @param Composer $composer
* @param IOInterface $io * @param IOInterface $io
* @param RepositoryInterface $globalRepository * @param Composer $composer
* @param Composer $globalComposer
*/ */
public function __construct(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null) public function __construct(IOInterface $io, Composer $composer, Composer $globalComposer = null)
{ {
$this->composer = $composer;
$this->io = $io; $this->io = $io;
$this->globalRepository = $globalRepository; $this->composer = $composer;
$this->globalComposer = $globalComposer;
$this->versionParser = new VersionParser(); $this->versionParser = new VersionParser();
} }
@ -62,12 +63,12 @@ class PluginManager
public function loadInstalledPlugins() public function loadInstalledPlugins()
{ {
$repo = $this->composer->getRepositoryManager()->getLocalRepository(); $repo = $this->composer->getRepositoryManager()->getLocalRepository();
$globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null;
if ($repo) { if ($repo) {
$this->loadRepository($repo); $this->loadRepository($repo);
} }
if ($this->globalRepository) { if ($globalRepo) {
$this->loadRepository($this->globalRepository); $this->loadRepository($globalRepo);
} }
} }
@ -206,11 +207,13 @@ class PluginManager
} }
$classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']); $classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
$pool = new Pool('dev');
$localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
$globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null;
$pool = new Pool('dev');
$pool->addRepository($localRepo); $pool->addRepository($localRepo);
if ($this->globalRepository) { if ($globalRepo) {
$pool->addRepository($this->globalRepository); $pool->addRepository($globalRepo);
} }
$autoloadPackages = array($package->getName() => $package); $autoloadPackages = array($package->getName() => $package);
@ -219,7 +222,7 @@ class PluginManager
$generator = $this->composer->getAutoloadGenerator(); $generator = $this->composer->getAutoloadGenerator();
$autoloads = array(); $autoloads = array();
foreach ($autoloadPackages as $autoloadPackage) { foreach ($autoloadPackages as $autoloadPackage) {
$downloadPath = $this->getInstallPath($autoloadPackage, ($this->globalRepository && $this->globalRepository->hasPackage($autoloadPackage))); $downloadPath = $this->getInstallPath($autoloadPackage, ($globalRepo && $globalRepo->hasPackage($autoloadPackage)));
$autoloads[] = array($autoloadPackage, $downloadPath); $autoloads[] = array($autoloadPackage, $downloadPath);
} }
@ -261,9 +264,6 @@ class PluginManager
return $this->composer->getInstallationManager()->getInstallPath($package); return $this->composer->getInstallationManager()->getInstallPath($package);
} }
$targetDir = $package->getTargetDir(); return $this->globalComposer->getInstallationManager()->getInstallPath($package);
$vendorDir = $this->composer->getConfig()->get('home').'/vendor';
return ($vendorDir ? $vendorDir.'/' : '').$package->getPrettyName().($targetDir ? '/'.$targetDir : '');
} }
} }

View File

@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository
$this->config = $config; $this->config = $config;
$this->options = $repoConfig['options']; $this->options = $repoConfig['options'];
$this->url = $repoConfig['url']; $this->url = $repoConfig['url'];
$this->baseUrl = rtrim(preg_replace('{^(.*)(?:/packages.json)?(?:[?#].*)?$}', '$1', $this->url), '/'); $this->baseUrl = rtrim(preg_replace('{^(.*)(?:/[^/\\]+.json)?(?:[?#].*)?$}', '$1', $this->url), '/');
$this->io = $io; $this->io = $io;
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$'); $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$');
$this->loader = new ArrayLoader(); $this->loader = new ArrayLoader();
@ -395,7 +395,7 @@ class ComposerRepository extends ArrayRepository
$jsonUrlParts = parse_url($this->url); $jsonUrlParts = parse_url($this->url);
if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '/packages.json')) { if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) {
$jsonUrl = $this->url; $jsonUrl = $this->url;
} else { } else {
$jsonUrl = $this->url . '/packages.json'; $jsonUrl = $this->url . '/packages.json';

View File

@ -93,7 +93,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
$composer = JsonFile::parseJson($composer, $resource); $composer = JsonFile::parseJson($composer, $resource);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
$changeset = JsonFile::parseJson($this->getContents($resource), $resource); $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
$composer['time'] = $changeset['timestamp']; $composer['time'] = $changeset['timestamp'];

View File

@ -156,7 +156,7 @@ class GitDriver extends VcsDriver
$composer = JsonFile::parseJson($composer, $resource); $composer = JsonFile::parseJson($composer, $resource);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
$date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
$composer['time'] = $date->format('Y-m-d H:i:s'); $composer['time'] = $date->format('Y-m-d H:i:s');
@ -227,7 +227,7 @@ class GitDriver extends VcsDriver
if (Filesystem::isLocalPath($url)) { if (Filesystem::isLocalPath($url)) {
$url = Filesystem::getPlatformPath($url); $url = Filesystem::getPlatformPath($url);
if (!is_dir($url)) { if (!is_dir($url)) {
throw new \RuntimeException('Directory does not exist: '.$url); return false;
} }
$process = new ProcessExecutor($io); $process = new ProcessExecutor($io);

View File

@ -171,7 +171,7 @@ class GitHubDriver extends VcsDriver
if ($composer) { if ($composer) {
$composer = JsonFile::parseJson($composer, $resource); $composer = JsonFile::parseJson($composer, $resource);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier); $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
$commit = JsonFile::parseJson($this->getContents($resource), $resource); $commit = JsonFile::parseJson($this->getContents($resource), $resource);
$composer['time'] = $commit['commit']['committer']['date']; $composer['time'] = $commit['commit']['committer']['date'];

View File

@ -102,7 +102,7 @@ class HgBitbucketDriver extends VcsDriver
$composer = JsonFile::parseJson($repoData['data'], $resource); $composer = JsonFile::parseJson($repoData['data'], $resource);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
$changeset = JsonFile::parseJson($this->getContents($resource), $resource); $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
$composer['time'] = $changeset['timestamp']; $composer['time'] = $changeset['timestamp'];

View File

@ -124,7 +124,7 @@ class HgDriver extends VcsDriver
$composer = JsonFile::parseJson($composer, $identifier); $composer = JsonFile::parseJson($composer, $identifier);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
$date = new \DateTime(trim($output), new \DateTimeZone('UTC')); $date = new \DateTime(trim($output), new \DateTimeZone('UTC'));
$composer['time'] = $date->format('Y-m-d H:i:s'); $composer['time'] = $date->format('Y-m-d H:i:s');
@ -200,7 +200,7 @@ class HgDriver extends VcsDriver
if (Filesystem::isLocalPath($url)) { if (Filesystem::isLocalPath($url)) {
$url = Filesystem::getPlatformPath($url); $url = Filesystem::getPlatformPath($url);
if (!is_dir($url)) { if (!is_dir($url)) {
throw new \RuntimeException('Directory does not exist: '.$url); return false;
} }
$process = new ProcessExecutor(); $process = new ProcessExecutor();

View File

@ -148,7 +148,7 @@ class SvnDriver extends VcsDriver
$composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev); $composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev);
if (!isset($composer['time'])) { if (empty($composer['time'])) {
$output = $this->execute('svn info', $this->baseUrl . $path . $rev); $output = $this->execute('svn info', $this->baseUrl . $path . $rev);
foreach ($this->process->splitLines($output) as $line) { foreach ($this->process->splitLines($output) as $line) {
if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) { if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) {

View File

@ -385,15 +385,19 @@ class RemoteFilesystem
protected function getOptionsForUrl($originUrl, $additionalOptions) protected function getOptionsForUrl($originUrl, $additionalOptions)
{ {
if (defined('HHVM_VERSION')) {
$phpVersion = 'HHVM ' . HHVM_VERSION;
} else {
$phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
}
$headers = array( $headers = array(
sprintf( sprintf(
'User-Agent: Composer/%s (%s; %s; PHP %s.%s.%s)', 'User-Agent: Composer/%s (%s; %s; %s)',
Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION, Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION,
php_uname('s'), php_uname('s'),
php_uname('r'), php_uname('r'),
PHP_MAJOR_VERSION, $phpVersion
PHP_MINOR_VERSION,
PHP_RELEASE_VERSION
) )
); );

View File

@ -43,6 +43,19 @@ final class StreamContextFactory
$proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']); $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
} }
// Override with HTTPS proxy if present and URL is https
if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
$proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']);
}
// Remove proxy if URL matches no_proxy directive
if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
$pattern = new NoProxyPattern($_SERVER['no_proxy']);
if ($pattern->test($url)) {
unset($proxy);
}
}
if (!empty($proxy)) { if (!empty($proxy)) {
$proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : ''; $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : '';
$proxyURL .= isset($proxy['host']) ? $proxy['host'] : ''; $proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
@ -64,16 +77,6 @@ final class StreamContextFactory
$options['http']['proxy'] = $proxyURL; $options['http']['proxy'] = $proxyURL;
// Handle no_proxy directive
if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
$pattern = new NoProxyPattern($_SERVER['no_proxy']);
if ($pattern->test($url)) {
unset($options['http']['proxy']);
}
}
// add request_fulluri and authentication if we still have a proxy to connect to
if (!empty($options['http']['proxy'])) {
// enabled request_fulluri unless it is explicitly disabled // enabled request_fulluri unless it is explicitly disabled
switch (parse_url($url, PHP_URL_SCHEME)) { switch (parse_url($url, PHP_URL_SCHEME)) {
case 'http': // default request_fulluri to true case 'http': // default request_fulluri to true
@ -90,6 +93,15 @@ final class StreamContextFactory
break; break;
} }
// add SNI opts for https URLs
if ('https' === parse_url($url, PHP_URL_SCHEME)) {
$options['ssl']['SNI_enabled'] = true;
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
$options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST);
}
}
// handle proxy auth if present
if (isset($proxy['user'])) { if (isset($proxy['user'])) {
$auth = urldecode($proxy['user']); $auth = urldecode($proxy['user']);
if (isset($proxy['pass'])) { if (isset($proxy['pass'])) {
@ -108,7 +120,6 @@ final class StreamContextFactory
} }
} }
} }
}
$options = array_replace_recursive($options, $defaultOptions); $options = array_replace_recursive($options, $defaultOptions);

View File

@ -21,6 +21,10 @@ class CacheTest extends TestCase
public function setUp() public function setUp()
{ {
if (getenv('TRAVIS')) {
$this->markTestSkipped('Test causes intermittent failures on Travis');
}
$this->root = sys_get_temp_dir() . '/composer_testdir'; $this->root = sys_get_temp_dir() . '/composer_testdir';
$this->ensureDirectoryExistsAndClear($this->root); $this->ensureDirectoryExistsAndClear($this->root);

View File

@ -247,4 +247,20 @@ class DefaultPolicyTest extends TestCase
return $map; return $map;
} }
public function testSelectLowest()
{
$policy = new DefaultPolicy(false, true);
$this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
$this->pool->addRepository($this->repo);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA1->getId());
$selected = $policy->selectPreferedPackages($this->pool, array(), $literals);
$this->assertEquals($expected, $selected);
}
} }

View File

@ -17,6 +17,7 @@ use Composer\EventDispatcher\EventDispatcher;
use Composer\Installer\InstallerEvents; use Composer\Installer\InstallerEvents;
use Composer\TestCase; use Composer\TestCase;
use Composer\Script\ScriptEvents; use Composer\Script\ScriptEvents;
use Composer\Script\CommandEvent;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
class EventDispatcherTest extends TestCase class EventDispatcherTest extends TestCase
@ -28,7 +29,7 @@ class EventDispatcherTest extends TestCase
{ {
$io = $this->getMock('Composer\IO\IOInterface'); $io = $this->getMock('Composer\IO\IOInterface');
$dispatcher = $this->getDispatcherStubForListenersTest(array( $dispatcher = $this->getDispatcherStubForListenersTest(array(
"Composer\Test\EventDispatcher\EventDispatcherTest::call" 'Composer\Test\EventDispatcher\EventDispatcherTest::call'
), $io); ), $io);
$io->expects($this->once()) $io->expects($this->once())
@ -38,6 +39,26 @@ class EventDispatcherTest extends TestCase
$dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false); $dispatcher->dispatchCommandEvent(ScriptEvents::POST_INSTALL_CMD, false);
} }
public function testDispatcherCanConvertScriptEventToCommandEventForListener()
{
$io = $this->getMock('Composer\IO\IOInterface');
$dispatcher = $this->getDispatcherStubForListenersTest(array(
'Composer\Test\EventDispatcher\EventDispatcherTest::expectsCommandEvent'
), $io);
$this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false));
}
public function testDispatcherDoesNotAttemptConversionForListenerWithoutTypehint()
{
$io = $this->getMock('Composer\IO\IOInterface');
$dispatcher = $this->getDispatcherStubForListenersTest(array(
'Composer\Test\EventDispatcher\EventDispatcherTest::expectsVariableEvent'
), $io);
$this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false));
}
/** /**
* @dataProvider getValidCommands * @dataProvider getValidCommands
* @param string $command * @param string $command
@ -205,6 +226,16 @@ class EventDispatcherTest extends TestCase
throw new \RuntimeException(); throw new \RuntimeException();
} }
public static function expectsCommandEvent(CommandEvent $event)
{
return false;
}
public static function expectsVariableEvent($event)
{
return false;
}
public static function someMethod() public static function someMethod()
{ {
return true; return true;

View File

@ -48,6 +48,7 @@ install --prefer-dist
"a/a": 20 "a/a": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -25,7 +25,8 @@ Requirements from the composer file are not installed if the lock file is presen
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--RUN-- --RUN--
install install

View File

@ -0,0 +1,44 @@
--TEST--
Install from a lock file that deleted a package
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "whitelisted", "version": "1.1.0" },
{ "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } },
{ "name": "fixed-dependency", "version": "1.1.0" },
{ "name": "fixed-dependency", "version": "1.0.0" },
{ "name": "old-dependency", "version": "1.0.0" }
]
}
],
"require": {
"whitelisted": "1.*",
"fixed-dependency": "1.*"
}
}
--LOCK--
{
"packages": [
{ "name": "whitelisted", "version": "1.1.0" },
{ "name": "fixed-dependency", "version": "1.0.0" }
],
"packages-dev": null,
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": false
}
--INSTALLED--
[
{ "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } },
{ "name": "fixed-dependency", "version": "1.0.0" },
{ "name": "old-dependency", "version": "1.0.0" }
]
--RUN--
install
--EXPECT--
Uninstalling old-dependency (1.0.0)
Updating whitelisted (1.0.0) to whitelisted (1.1.0)

View File

@ -33,7 +33,8 @@ Installing an old alias that doesn't exist anymore from a lock is possible
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--RUN-- --RUN--
install install

View File

@ -36,6 +36,7 @@ Partial update from lock file should apply lock file and downgrade unstable pack
"b/unstable": 15 "b/unstable": 15
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }
@ -59,6 +60,7 @@ update c/uptodate
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -36,6 +36,7 @@ Partial update from lock file should update everything to the state of the lock,
"b/unstable": 15 "b/unstable": 15
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }
@ -59,6 +60,7 @@ update b/unstable
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -43,6 +43,7 @@ update b/unstable
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -39,7 +39,8 @@ Update aliased package does not mess up the lock file
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--INSTALLED-- --INSTALLED--
[ [
@ -66,6 +67,7 @@ update
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -0,0 +1,40 @@
--TEST--
Updates packages to their lowest stable version
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "a/a", "version": "1.0.0-rc1" },
{ "name": "a/a", "version": "1.0.1" },
{ "name": "a/a", "version": "1.1.0" },
{ "name": "a/b", "version": "1.0.0" },
{ "name": "a/b", "version": "1.0.1" },
{ "name": "a/b", "version": "2.0.0" },
{ "name": "a/c", "version": "1.0.0" },
{ "name": "a/c", "version": "2.0.0" }
]
}
],
"require": {
"a/a": "~1.0@dev",
"a/c": "2.*"
},
"require-dev": {
"a/b": "*"
}
}
--INSTALLED--
[
{ "name": "a/a", "version": "1.0.0-rc1" },
{ "name": "a/c", "version": "2.0.0" },
{ "name": "a/b", "version": "1.0.1" }
]
--RUN--
update --prefer-lowest --prefer-stable
--EXPECT--
Updating a/a (1.0.0-rc1) to a/a (1.0.1)
Updating a/b (1.0.1) to a/b (1.0.0)

View File

@ -32,7 +32,8 @@ Limited update takes rules from lock if available, and not from the installed re
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--INSTALLED-- --INSTALLED--
[ [

View File

@ -0,0 +1,32 @@
--TEST--
Update with a package whitelist removes unused packages
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "whitelisted", "version": "1.1.0" },
{ "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } },
{ "name": "fixed-dependency", "version": "1.1.0" },
{ "name": "fixed-dependency", "version": "1.0.0" },
{ "name": "old-dependency", "version": "1.0.0" }
]
}
],
"require": {
"whitelisted": "1.*",
"fixed-dependency": "1.*"
}
}
--INSTALLED--
[
{ "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } },
{ "name": "fixed-dependency", "version": "1.0.0" },
{ "name": "old-dependency", "version": "1.0.0" }
]
--RUN--
update --with-dependencies whitelisted
--EXPECT--
Uninstalling old-dependency (1.0.0)
Updating whitelisted (1.0.0) to whitelisted (1.1.0)

View File

@ -20,7 +20,8 @@ Installing locked dev packages should remove old dependencies
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": [],
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--INSTALLED-- --INSTALLED--
[ [

View File

@ -32,7 +32,8 @@ Updating a dev package for new reference updates the url and reference
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": {"a/a":20}, "stability-flags": {"a/a":20},
"prefer-stable": false "prefer-stable": false,
"prefer-lowest": false
} }
--INSTALLED-- --INSTALLED--
[ [
@ -59,6 +60,7 @@ update
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": {"a/a":20}, "stability-flags": {"a/a":20},
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }

View File

@ -217,6 +217,8 @@ class InstallerTest extends TestCase
->setDryRun($input->getOption('dry-run')) ->setDryRun($input->getOption('dry-run'))
->setUpdateWhitelist($input->getArgument('packages')) ->setUpdateWhitelist($input->getArgument('packages'))
->setWhitelistDependencies($input->getOption('with-dependencies')) ->setWhitelistDependencies($input->getOption('with-dependencies'))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'));
return $installer->run(); return $installer->run();
@ -292,7 +294,7 @@ class InstallerTest extends TestCase
// Change paths like file://foobar to file:///path/to/fixtures // Change paths like file://foobar to file:///path/to/fixtures
if (preg_match('{^file://[^/]}', $repo['url'])) { if (preg_match('{^file://[^/]}', $repo['url'])) {
$repo['url'] = "file://${fixturesDir}/" . substr($repo['url'], 7); $repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7);
} }
unset($repo); unset($repo);

View File

@ -73,6 +73,7 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
), ),
array( array(
'{ '{
"empty": "",
"require": { "require": {
"foo": "bar" "foo": "bar"
} }
@ -81,6 +82,7 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
'vendor/baz', 'vendor/baz',
'qux', 'qux',
'{ '{
"empty": "",
"require": { "require": {
"foo": "bar", "foo": "bar",
"vendor/baz": "qux" "vendor/baz": "qux"
@ -281,6 +283,58 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
); );
} }
/**
* @dataProvider providerAddLinkAndSortPackages
*/
public function testAddLinkAndSortPackages($json, $type, $package, $constraint, $sortPackages, $expected)
{
$manipulator = new JsonManipulator($json);
$this->assertTrue($manipulator->addLink($type, $package, $constraint, $sortPackages));
$this->assertEquals($expected, $manipulator->getContents());
}
public function providerAddLinkAndSortPackages()
{
return array(
array(
'{
"require": {
"vendor/baz": "qux"
}
}',
'require',
'foo',
'bar',
true,
'{
"require": {
"foo": "bar",
"vendor/baz": "qux"
}
}
'
),
array(
'{
"require": {
"vendor/baz": "qux"
}
}',
'require',
'foo',
'bar',
false,
'{
"require": {
"vendor/baz": "qux",
"foo": "bar"
}
}
'
),
);
}
/** /**
* @dataProvider removeSubNodeProvider * @dataProvider removeSubNodeProvider
*/ */

View File

@ -16,6 +16,7 @@ use Composer\Config;
use Composer\Factory; use Composer\Factory;
use Composer\Repository; use Composer\Repository;
use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryManager;
use Composer\Repository\WritableRepositoryInterface;
use Composer\Installer; use Composer\Installer;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
@ -46,7 +47,7 @@ class FactoryMock extends Factory
{ {
} }
protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im)
{ {
} }
} }

View File

@ -135,9 +135,10 @@ class LockerTest extends \PHPUnit_Framework_TestCase
'platform' => array(), 'platform' => array(),
'platform-dev' => array(), 'platform-dev' => array(),
'prefer-stable' => false, 'prefer-stable' => false,
'prefer-lowest' => false,
)); ));
$locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false); $locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false);
} }
public function testLockBadPackages() public function testLockBadPackages()
@ -156,7 +157,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase
$this->setExpectedException('LogicException'); $this->setExpectedException('LogicException');
$locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false); $locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false);
} }
public function testIsFresh() public function testIsFresh()

View File

@ -90,6 +90,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
'forces w.x.y.z/2' => array('0', '0.0.0.0'), 'forces w.x.y.z/2' => array('0', '0.0.0.0'),
'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'),
'parses long/2' => array('10.4.13beta2', '10.4.13.0-beta2'), 'parses long/2' => array('10.4.13beta2', '10.4.13.0-beta2'),
'parses long/semver' => array('10.4.13beta.2', '10.4.13.0-beta2'),
'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'), 'expand shorthand' => array('10.4.13-b', '10.4.13.0-beta'),
'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'), 'expand shorthand2' => array('10.4.13-b5', '10.4.13.0-beta5'),
'strips leading v' => array('v1.0.0', '1.0.0.0'), 'strips leading v' => array('v1.0.0', '1.0.0.0'),
@ -109,6 +110,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
'parses arbitrary2' => array('DEV-FOOBAR', 'dev-FOOBAR'), 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-FOOBAR'),
'parses arbitrary3' => array('dev-feature/foo', 'dev-feature/foo'), 'parses arbitrary3' => array('dev-feature/foo', 'dev-feature/foo'),
'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'),
'semver metadata' => array('dev-master+foo.bar', '9999999-dev'),
'semver metadata/2' => array('1.0.0-beta.5+foo', '1.0.0.0-beta5'),
'semver metadata/3' => array('1.0.0+foo', '1.0.0.0'),
'metadata w/ alias' => array('1.0.0+foo as 2.0', '1.0.0.0'),
); );
} }
@ -130,6 +135,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
'invalid type' => array('1.0.0-meh'), 'invalid type' => array('1.0.0-meh'),
'too many bits' => array('1.0.0.0.0'), 'too many bits' => array('1.0.0.0.0'),
'non-dev arbitrary' => array('feature-foo'), 'non-dev arbitrary' => array('feature-foo'),
'metadata w/ space' => array('1.0.0+foo bar'),
); );
} }
@ -208,7 +214,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
'match any' => array('*', new EmptyConstraint()), 'match any' => array('*', new EmptyConstraint()),
'match any/2' => array('*.*', new EmptyConstraint()), 'match any/2' => array('*.*', new EmptyConstraint()),
'match any/3' => array('*.x.*', new EmptyConstraint()), 'match any/3' => array('*.x.*', new EmptyConstraint()),
'match any/4' => array('x.x.x.*', new EmptyConstraint()), 'match any/4' => array('x.X.x.*', new EmptyConstraint()),
'not equal' => array('<>1.0.0', new VersionConstraint('<>', '1.0.0.0')), 'not equal' => array('<>1.0.0', new VersionConstraint('<>', '1.0.0.0')),
'not equal/2' => array('!=1.0.0', new VersionConstraint('!=', '1.0.0.0')), 'not equal/2' => array('!=1.0.0', new VersionConstraint('!=', '1.0.0.0')),
'greater than' => array('>1.0.0', new VersionConstraint('>', '1.0.0.0')), 'greater than' => array('>1.0.0', new VersionConstraint('>', '1.0.0.0')),
@ -221,6 +227,8 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')), 'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')),
'shorthand beta' => array('1.2.3b5', new VersionConstraint('=', '1.2.3.0-beta5')), 'shorthand beta' => array('1.2.3b5', new VersionConstraint('=', '1.2.3.0-beta5')),
'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')), 'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')),
'accepts spaces/2' => array('< 1.2.3', new VersionConstraint('<', '1.2.3.0-dev')),
'accepts spaces/3' => array('> 1.2.3', new VersionConstraint('>', '1.2.3.0')),
'accepts master' => array('>=dev-master', new VersionConstraint('>=', '9999999-dev')), 'accepts master' => array('>=dev-master', new VersionConstraint('>=', '9999999-dev')),
'accepts master/2' => array('dev-master', new VersionConstraint('=', '9999999-dev')), 'accepts master/2' => array('dev-master', new VersionConstraint('=', '9999999-dev')),
'accepts arbitrary' => array('dev-feature-a', new VersionConstraint('=', 'dev-feature-a')), 'accepts arbitrary' => array('dev-feature-a', new VersionConstraint('=', 'dev-feature-a')),
@ -253,7 +261,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
array('20.*', new VersionConstraint('>=', '20.0.0.0-dev'), new VersionConstraint('<', '21.0.0.0-dev')), array('20.*', new VersionConstraint('>=', '20.0.0.0-dev'), new VersionConstraint('<', '21.0.0.0-dev')),
array('2.0.*', new VersionConstraint('>=', '2.0.0.0-dev'), new VersionConstraint('<', '2.1.0.0-dev')), array('2.0.*', new VersionConstraint('>=', '2.0.0.0-dev'), new VersionConstraint('<', '2.1.0.0-dev')),
array('2.2.x', new VersionConstraint('>=', '2.2.0.0-dev'), new VersionConstraint('<', '2.3.0.0-dev')), array('2.2.x', new VersionConstraint('>=', '2.2.0.0-dev'), new VersionConstraint('<', '2.3.0.0-dev')),
array('2.10.x', new VersionConstraint('>=', '2.10.0.0-dev'), new VersionConstraint('<', '2.11.0.0-dev')), array('2.10.X', new VersionConstraint('>=', '2.10.0.0-dev'), new VersionConstraint('<', '2.11.0.0-dev')),
array('2.1.3.*', new VersionConstraint('>=', '2.1.3.0-dev'), new VersionConstraint('<', '2.1.4.0-dev')), array('2.1.3.*', new VersionConstraint('>=', '2.1.3.0-dev'), new VersionConstraint('<', '2.1.4.0-dev')),
array('0.*', null, new VersionConstraint('<', '1.0.0.0-dev')), array('0.*', null, new VersionConstraint('<', '1.0.0.0-dev')),
); );
@ -291,16 +299,112 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testParseConstraintsMulti() /**
* @dataProvider caretConstraints
*/
public function testParseCaretWildcard($input, $min, $max)
{
$parser = new VersionParser;
if ($min) {
$expected = new MultiConstraint(array($min, $max));
} else {
$expected = $max;
}
$this->assertSame((string) $expected, (string) $parser->parseConstraints($input));
}
public function caretConstraints()
{
return array(
array('^1', new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
array('^0', new VersionConstraint('>=', '0.0.0.0-dev'), new VersionConstraint('<', '1.0.0.0-dev')),
array('^0.0', new VersionConstraint('>=', '0.0.0.0-dev'), new VersionConstraint('<', '0.1.0.0-dev')),
array('^1.2', new VersionConstraint('>=', '1.2.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
array('^1.2.3-beta.2', new VersionConstraint('>=', '1.2.3.0-beta2'), new VersionConstraint('<', '2.0.0.0-dev')),
array('^1.2.3.4', new VersionConstraint('>=', '1.2.3.4-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
array('^1.2.3', new VersionConstraint('>=', '1.2.3.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
array('^0.2.3', new VersionConstraint('>=', '0.2.3.0-dev'), new VersionConstraint('<', '0.3.0.0-dev')),
array('^0.2', new VersionConstraint('>=', '0.2.0.0-dev'), new VersionConstraint('<', '0.3.0.0-dev')),
array('^0.0.3', new VersionConstraint('>=', '0.0.3.0-dev'), new VersionConstraint('<', '0.0.4.0-dev')),
array('^0.0.3-alpha', new VersionConstraint('>=', '0.0.3.0-alpha'), new VersionConstraint('<', '0.0.4.0-dev')),
array('^0.0.3-dev', new VersionConstraint('>=', '0.0.3.0-dev'), new VersionConstraint('<', '0.0.4.0-dev')),
);
}
/**
* @dataProvider hyphenConstraints
*/
public function testParseHyphen($input, $min, $max)
{
$parser = new VersionParser;
if ($min) {
$expected = new MultiConstraint(array($min, $max));
} else {
$expected = $max;
}
$this->assertSame((string) $expected, (string) $parser->parseConstraints($input));
}
public function hyphenConstraints()
{
return array(
array('1 - 2', new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '3.0.0.0-dev')),
array('1.2.3 - 2.3.4.5', new VersionConstraint('>=', '1.2.3.0-dev'), new VersionConstraint('<=', '2.3.4.5')),
array('1.2-beta - 2.3', new VersionConstraint('>=', '1.2.0.0-beta'), new VersionConstraint('<', '2.4.0.0-dev')),
array('1.2-beta - 2.3-dev', new VersionConstraint('>=', '1.2.0.0-beta'), new VersionConstraint('<=', '2.3.0.0-dev')),
array('1.2-RC - 2.3.1', new VersionConstraint('>=', '1.2.0.0-RC'), new VersionConstraint('<=', '2.3.1.0')),
array('1.2.3-alpha - 2.3-RC', new VersionConstraint('>=', '1.2.3.0-alpha'), new VersionConstraint('<=', '2.3.0.0-RC')),
);
}
/**
* @dataProvider multiConstraintProvider
*/
public function testParseConstraintsMulti($constraint)
{ {
$parser = new VersionParser; $parser = new VersionParser;
$first = new VersionConstraint('>', '2.0.0.0'); $first = new VersionConstraint('>', '2.0.0.0');
$second = new VersionConstraint('<=', '3.0.0.0'); $second = new VersionConstraint('<=', '3.0.0.0');
$multi = new MultiConstraint(array($first, $second)); $multi = new MultiConstraint(array($first, $second));
$this->assertSame((string) $multi, (string) $parser->parseConstraints('>2.0,<=3.0')); $this->assertSame((string) $multi, (string) $parser->parseConstraints($constraint));
} }
public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive() public function multiConstraintProvider()
{
return array(
array('>2.0,<=3.0'),
array('>2.0 <=3.0'),
array('>2.0 <=3.0'),
array('>2.0, <=3.0'),
array('>2.0 ,<=3.0'),
array('>2.0 , <=3.0'),
array('>2.0 , <=3.0'),
array('> 2.0 <= 3.0'),
array('> 2.0 , <= 3.0'),
array(' > 2.0 , <= 3.0 '),
);
}
public function testParseConstraintsMultiWithStabilitySuffix()
{
$parser = new VersionParser;
$first = new VersionConstraint('>=', '1.1.0.0-alpha4');
$second = new VersionConstraint('<', '1.2.9999999.9999999-dev');
$multi = new MultiConstraint(array($first, $second));
$this->assertSame((string) $multi, (string) $parser->parseConstraints('>=1.1.0-alpha4,<1.2.x-dev'));
$first = new VersionConstraint('>=', '1.1.0.0-alpha4');
$second = new VersionConstraint('<', '1.2.0.0-beta2');
$multi = new MultiConstraint(array($first, $second));
$this->assertSame((string) $multi, (string) $parser->parseConstraints('>=1.1.0-alpha4,<1.2-beta2'));
}
/**
* @dataProvider multiConstraintProvider2
*/
public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive($constraint)
{ {
$parser = new VersionParser; $parser = new VersionParser;
$first = new VersionConstraint('>', '2.0.0.0'); $first = new VersionConstraint('>', '2.0.0.0');
@ -308,7 +412,16 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
$third = new VersionConstraint('>', '2.0.6.0'); $third = new VersionConstraint('>', '2.0.6.0');
$multi1 = new MultiConstraint(array($first, $second)); $multi1 = new MultiConstraint(array($first, $second));
$multi2 = new MultiConstraint(array($multi1, $third), false); $multi2 = new MultiConstraint(array($multi1, $third), false);
$this->assertSame((string) $multi2, (string) $parser->parseConstraints('>2.0,<2.0.5 | >2.0.6')); $this->assertSame((string) $multi2, (string) $parser->parseConstraints($constraint));
}
public function multiConstraintProvider2()
{
return array(
array('>2.0,<2.0.5 | >2.0.6'),
array('>2.0,<2.0.5 || >2.0.6'),
array('> 2.0 , <2.0.5 | > 2.0.6'),
);
} }
public function testParseConstraintsMultiWithStabilities() public function testParseConstraintsMultiWithStabilities()
@ -335,6 +448,9 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
return array( return array(
'empty ' => array(''), 'empty ' => array(''),
'invalid version' => array('1.0.0-meh'), 'invalid version' => array('1.0.0-meh'),
'operator abuse' => array('>2.0,,<=3.0'),
'operator abuse/2' => array('>2.0 ,, <=3.0'),
'operator abuse/3' => array('>2.0 ||| <=3.0'),
); );
} }

View File

@ -98,7 +98,10 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
array('v1.2.1', false, 'stable', '~1.2'), array('v1.2.1', false, 'stable', '~1.2'),
array('3.1.2-pl2', false, 'stable', '~3.1'), array('3.1.2-pl2', false, 'stable', '~3.1'),
array('3.1.2-patch', false, 'stable', '~3.1'), array('3.1.2-patch', false, 'stable', '~3.1'),
// for non-stable versions, we add ~, but don't try the (1.2.1 -> 1.2) transformation array('0.1.0', false, 'stable', '0.1.*'),
array('0.1.3', false, 'stable', '0.1.*'),
array('0.0.3', false, 'stable', '0.0.3.*'),
array('0.0.3-alpha', false, 'alpha', '0.0.3.*@alpha'),
array('2.0-beta.1', false, 'beta', '~2.0@beta'), array('2.0-beta.1', false, 'beta', '~2.0@beta'),
array('3.1.2-alpha5', false, 'alpha', '~3.1@alpha'), array('3.1.2-alpha5', false, 'alpha', '~3.1@alpha'),
array('3.0-RC2', false, 'RC', '~3.0@RC'), array('3.0-RC2', false, 'RC', '~3.0@RC'),

View File

@ -76,7 +76,7 @@ class PluginInstallerTest extends \PHPUnit_Framework_TestCase
$this->composer->setInstallationManager($im); $this->composer->setInstallationManager($im);
$this->composer->setAutoloadGenerator($this->autoloadGenerator); $this->composer->setAutoloadGenerator($this->autoloadGenerator);
$this->pm = new PluginManager($this->composer, $this->io); $this->pm = new PluginManager($this->io, $this->composer);
$this->composer->setPluginManager($this->pm); $this->composer->setPluginManager($this->pm);
$config->merge(array( $config->merge(array(

View File

@ -20,6 +20,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
{ {
unset($_SERVER['HTTP_PROXY']); unset($_SERVER['HTTP_PROXY']);
unset($_SERVER['http_proxy']); unset($_SERVER['http_proxy']);
unset($_SERVER['HTTPS_PROXY']);
unset($_SERVER['https_proxy']);
unset($_SERVER['no_proxy']); unset($_SERVER['no_proxy']);
} }
@ -27,6 +29,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
{ {
unset($_SERVER['HTTP_PROXY']); unset($_SERVER['HTTP_PROXY']);
unset($_SERVER['http_proxy']); unset($_SERVER['http_proxy']);
unset($_SERVER['HTTPS_PROXY']);
unset($_SERVER['https_proxy']);
unset($_SERVER['no_proxy']); unset($_SERVER['no_proxy']);
} }
@ -126,17 +130,56 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
{ {
$_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net';
$context = StreamContextFactory::getContext('http://example.org', array('http' => array('method' => 'GET'))); $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET')));
$options = stream_context_get_options($context); $options = stream_context_get_options($context);
$this->assertEquals(array('http' => array( $expected = array(
'http' => array(
'proxy' => 'tcp://proxyserver.net:80', 'proxy' => 'tcp://proxyserver.net:80',
'request_fulluri' => true, 'request_fulluri' => true,
'method' => 'GET', 'method' => 'GET',
'header' => array("Proxy-Authorization: Basic " . base64_encode('username:password')), 'header' => array("Proxy-Authorization: Basic " . base64_encode('username:password')),
'max_redirects' => 20, 'max_redirects' => 20,
'follow_location' => 1, 'follow_location' => 1,
)), $options); ), 'ssl' => array(
'SNI_enabled' => true,
'SNI_server_name' => 'example.org'
)
);
if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
unset($expected['ssl']['SNI_server_name']);
}
$this->assertEquals($expected, $options);
}
public function testHttpsProxyOverride()
{
if (!extension_loaded('openssl')) {
$this->markTestSkipped('Requires openssl');
}
$_SERVER['http_proxy'] = 'http://username:password@proxyserver.net';
$_SERVER['https_proxy'] = 'https://woopproxy.net';
$context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET')));
$options = stream_context_get_options($context);
$expected = array(
'http' => array(
'proxy' => 'ssl://woopproxy.net:443',
'request_fulluri' => true,
'method' => 'GET',
'max_redirects' => 20,
'follow_location' => 1,
), 'ssl' => array(
'SNI_enabled' => true,
'SNI_server_name' => 'example.org'
)
);
if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
unset($expected['ssl']['SNI_server_name']);
}
$this->assertEquals($expected, $options);
} }
/** /**