Merge remote-tracking branch 'upstream/master'
Conflicts: src/Composer/Downloader/FileDownloader.phppull/2189/head
commit
0f2c0ab389
13
.travis.yml
13
.travis.yml
|
@ -5,11 +5,18 @@ php:
|
|||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- hhvm
|
||||
|
||||
before_script:
|
||||
- echo '' > ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
before_script:
|
||||
- sudo apt-get install parallel
|
||||
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
|
||||
- composer install --dev --prefer-source
|
||||
- git config --global user.name travis-ci
|
||||
- git config --global user.email travis@example.com
|
||||
|
||||
script: ./vendor/bin/phpunit -c tests/complete.phpunit.xml
|
||||
script:
|
||||
- ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml {};' || exit 1
|
||||
|
|
23
README.md
23
README.md
|
@ -16,10 +16,10 @@ Installation / Usage
|
|||
$ curl -sS https://getcomposer.org/installer | php
|
||||
```
|
||||
|
||||
|
||||
2. Create a composer.json defining your dependencies. Note that this example is
|
||||
a short version for applications that are not meant to be published as packages
|
||||
themselves. To create libraries/packages please read the [guidelines](https://packagist.org/about).
|
||||
themselves. To create libraries/packages please read the
|
||||
[documentation](http://getcomposer.org/doc/02-libraries.md).
|
||||
|
||||
``` json
|
||||
{
|
||||
|
@ -47,24 +47,7 @@ You can now run Composer by executing the `bin/composer` script: `php /path/to/c
|
|||
Global installation of Composer (manual)
|
||||
----------------------------------------
|
||||
|
||||
Since Composer works with the current working directory it is possible to install it
|
||||
in a system wide way.
|
||||
|
||||
1. Change into a directory in your path like `cd /usr/local/bin`
|
||||
2. Get Composer `curl -sS https://getcomposer.org/installer | php`
|
||||
3. Make the phar executable `chmod a+x composer.phar`
|
||||
4. Change into a project directory `cd /path/to/my/project`
|
||||
5. Use Composer as you normally would `composer.phar install`
|
||||
6. Optionally you can rename the composer.phar to composer to make it easier
|
||||
|
||||
Global installation of Composer (via homebrew)
|
||||
----------------------------------------------
|
||||
|
||||
Composer is part of the homebrew-php project.
|
||||
|
||||
1. Tap the homebrew-php repository into your brew installation if you haven't done yet: `brew tap josegonzalez/homebrew-php`
|
||||
2. Run `brew install josegonzalez/php/composer`.
|
||||
3. Use Composer with the `composer` command.
|
||||
Follow instructions [in the documentation](http://getcomposer.org/doc/00-intro.md#globally)
|
||||
|
||||
Updating Composer
|
||||
-----------------
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"seld/jsonlint": "1.*",
|
||||
"symfony/console": "~2.3",
|
||||
"symfony/finder": "~2.2",
|
||||
"symfony/process": "~2.1"
|
||||
"symfony/process": "~2.1@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~3.7.10"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
|
||||
],
|
||||
"hash": "370b764a9317165e8ea7a2e1623e031b",
|
||||
"hash": "4494d3567c8c22b1adaded932825b969",
|
||||
"packages": [
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
|
@ -32,16 +32,16 @@
|
|||
},
|
||||
{
|
||||
"name": "seld/jsonlint",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/Seldaek/jsonlint",
|
||||
"reference": "1.1.1"
|
||||
"url": "https://github.com/Seldaek/jsonlint.git",
|
||||
"reference": "7cd4c4965e17e6e4c07f26d566619a4c76f8c672"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1.1.1",
|
||||
"reference": "1.1.1",
|
||||
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/7cd4c4965e17e6e4c07f26d566619a4c76f8c672",
|
||||
"reference": "7cd4c4965e17e6e4c07f26d566619a4c76f8c672",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -75,21 +75,21 @@
|
|||
"parser",
|
||||
"validator"
|
||||
],
|
||||
"time": "2013-02-11 23:03:12"
|
||||
"time": "2013-11-04 15:41:11"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.3.3",
|
||||
"version": "v2.3.7",
|
||||
"target-dir": "Symfony/Component/Console",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Console.git",
|
||||
"reference": "v2.3.3"
|
||||
"reference": "00848d3e13cf512e77c7498c2b3b0192f61f4b18"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Console/zipball/v2.3.3",
|
||||
"reference": "v2.3.3",
|
||||
"url": "https://api.github.com/repos/symfony/Console/zipball/00848d3e13cf512e77c7498c2b3b0192f61f4b18",
|
||||
"reference": "00848d3e13cf512e77c7498c2b3b0192f61f4b18",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -128,21 +128,21 @@
|
|||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-07-21 12:12:18"
|
||||
"time": "2013-11-13 21:27:40"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v2.3.3",
|
||||
"version": "v2.3.7",
|
||||
"target-dir": "Symfony/Component/Finder",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Finder.git",
|
||||
"reference": "v2.3.3"
|
||||
"reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Finder/zipball/v2.3.3",
|
||||
"reference": "v2.3.3",
|
||||
"url": "https://api.github.com/repos/symfony/Finder/zipball/a175521f680b178e63c5d0ab87c6b046c0990c3f",
|
||||
"reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -175,21 +175,21 @@
|
|||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-07-21 12:12:18"
|
||||
"time": "2013-09-19 09:45:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v2.3.3",
|
||||
"version": "dev-master",
|
||||
"target-dir": "Symfony/Component/Process",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Process.git",
|
||||
"reference": "v2.3.3"
|
||||
"reference": "88ccdd7a1fb67a23fe81355c66a09d6908ebcd80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Process/zipball/v2.3.3",
|
||||
"reference": "v2.3.3",
|
||||
"url": "https://api.github.com/repos/symfony/Process/zipball/88ccdd7a1fb67a23fe81355c66a09d6908ebcd80",
|
||||
"reference": "88ccdd7a1fb67a23fe81355c66a09d6908ebcd80",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -198,7 +198,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3-dev"
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -222,22 +222,22 @@
|
|||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-08-02 21:51:01"
|
||||
"time": "2013-11-09 12:03:12"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "1.2.12",
|
||||
"version": "1.2.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "1.2.12"
|
||||
"reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.12",
|
||||
"reference": "1.2.12",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94",
|
||||
"reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -285,20 +285,20 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2013-07-06 06:26:16"
|
||||
"time": "2013-09-10 08:14:32"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
"version": "1.3.3",
|
||||
"version": "1.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/sebastianbergmann/php-file-iterator.git",
|
||||
"reference": "1.3.3"
|
||||
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
|
||||
"reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3",
|
||||
"reference": "1.3.3",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
|
||||
"reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -325,12 +325,12 @@
|
|||
}
|
||||
],
|
||||
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
|
||||
"homepage": "http://www.phpunit.de/",
|
||||
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
|
||||
"keywords": [
|
||||
"filesystem",
|
||||
"iterator"
|
||||
],
|
||||
"time": "2012-10-11 04:44:38"
|
||||
"time": "2013-10-10 15:34:57"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-text-template",
|
||||
|
@ -382,12 +382,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-timer.git",
|
||||
"reference": "1.0.5"
|
||||
"reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1.0.5",
|
||||
"reference": "1.0.5",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
|
||||
"reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -422,16 +422,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-token-stream",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
|
||||
"reference": "1.2.0"
|
||||
"reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1.2.0",
|
||||
"reference": "1.2.0",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e",
|
||||
"reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -468,20 +468,20 @@
|
|||
"keywords": [
|
||||
"tokenizer"
|
||||
],
|
||||
"time": "2013-08-04 05:57:48"
|
||||
"time": "2013-09-13 04:58:23"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "3.7.24",
|
||||
"version": "3.7.28",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "3.7.24"
|
||||
"reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.24",
|
||||
"reference": "3.7.24",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d",
|
||||
"reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -542,7 +542,7 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2013-08-09 06:58:24"
|
||||
"time": "2013-10-17 07:27:40"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
|
@ -595,17 +595,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.3.3",
|
||||
"version": "v2.3.7",
|
||||
"target-dir": "Symfony/Component/Yaml",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Yaml.git",
|
||||
"reference": "v2.3.3"
|
||||
"reference": "c1bda5b459d792cb253de12c65beba3040163b2b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.3.3",
|
||||
"reference": "v2.3.3",
|
||||
"url": "https://api.github.com/repos/symfony/Yaml/zipball/c1bda5b459d792cb253de12c65beba3040163b2b",
|
||||
"reference": "c1bda5b459d792cb253de12c65beba3040163b2b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -638,16 +638,16 @@
|
|||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-07-21 12:12:18"
|
||||
"time": "2013-10-17 11:48:01"
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [
|
||||
|
||||
],
|
||||
"stability-flags": {
|
||||
"symfony/process": 20
|
||||
},
|
||||
"platform": {
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
|
|
|
@ -91,6 +91,18 @@ You can run these commands to easily access `composer` from anywhere on your sys
|
|||
|
||||
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.
|
||||
|
||||
1. Tap the homebrew-php repository into your brew installation if you haven't done
|
||||
so yet: `brew tap josegonzalez/homebrew-php`
|
||||
2. Run `brew install josegonzalez/php/composer`.
|
||||
3. Use Composer with the `composer` command.
|
||||
|
||||
> **Note:** If you receive an error saying PHP53 or higher is missing use this command to install php
|
||||
> `brew install php53-intl`
|
||||
|
||||
## Installation - Windows
|
||||
|
||||
### Using the Installer
|
||||
|
@ -108,7 +120,7 @@ composer.phar:
|
|||
|
||||
C:\Users\username>cd C:\bin
|
||||
C:\bin>php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
|
||||
|
||||
|
||||
> **Note:** If the above fails due to file_get_contents, use the `http` url or enable php_openssl.dll in php.ini
|
||||
|
||||
Create a new `composer.bat` file alongside `composer.phar`:
|
||||
|
|
|
@ -58,31 +58,31 @@ smaller decoupled parts.
|
|||
|
||||
### Package Versions
|
||||
|
||||
We are requiring version `1.0.*` of monolog. This means any version in the `1.0`
|
||||
development branch. It would match `1.0.0`, `1.0.2` or `1.0.20`.
|
||||
In the previous example we were requiring version `1.0.*` of monolog. This
|
||||
means any version in the `1.0` development branch. It would match `1.0.0`,
|
||||
`1.0.2` or `1.0.20`.
|
||||
|
||||
Version constraints can be specified in a few different ways.
|
||||
|
||||
* **Exact version:** You can specify the exact version of a package, for
|
||||
example `1.0.2`.
|
||||
Name | Example | Description
|
||||
-------------- | --------------------- | -----------
|
||||
Exact version | `1.0.2` | You can specify the exact version of a package.
|
||||
Range | `>=1.0` `>=1.0,<2.0` `>=1.0,<1.1 | >=1.2` | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`. <br />You can define multiple ranges, separated by a comma, which will be treated as a **logical AND**. A pipe symbol `|` will be treated as a **logical OR**. <br />AND has higher precedence than OR.
|
||||
Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1`.
|
||||
Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2,<2.0`. For more details, read the next section below.
|
||||
|
||||
* **Range:** By using comparison operators you can specify ranges of valid
|
||||
versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`. An example range
|
||||
would be `>=1.0`. You can define multiple ranges, separated by a comma:
|
||||
`>=1.0,<2.0`.
|
||||
### Next Significant Release (Tilde Operator)
|
||||
|
||||
* **Wildcard:** You can specify a pattern with a `*` wildcard. `1.0.*` is the
|
||||
equivalent of `>=1.0,<1.1`.
|
||||
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
|
||||
it is mostly useful for projects respecting [semantic
|
||||
versioning](http://semver.org/). A common usage would be to mark the minimum
|
||||
minor version you depend on, like `~1.2` (which allows anything up to, but not
|
||||
including, 2.0). Since in theory there should be no backwards compatibility
|
||||
breaks until 2.0, that works well. Another way of looking at it is that using
|
||||
`~` specifies a minimum version, but allows the last digit specified to go up.
|
||||
|
||||
* **Next Significant Release (Tilde Operator):** 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 it is mostly useful for projects
|
||||
respecting [semantic versioning](http://semver.org/). A common usage would be
|
||||
to mark the minimum minor version you depend on, like `~1.2` (which allows
|
||||
anything up to, but not including, 2.0). Since in theory there should be no
|
||||
backwards compatibility breaks until 2.0, that works well. Another way of
|
||||
looking at it is that using `~` specifies a minimum version, but allows the
|
||||
last digit specified to go up.
|
||||
### Stability
|
||||
|
||||
By default only stable releases are taken into consideration. If you would like
|
||||
to also get RC, beta, alpha or dev versions of your dependencies you can do
|
||||
|
|
|
@ -70,8 +70,8 @@ you can just add a `version` field:
|
|||
|
||||
For every tag that looks like a version, a package version of that tag will be
|
||||
created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix
|
||||
of `-dev`, `-patch`, `-alpha`, `-beta` or `-RC`. The patch, alpha, beta and
|
||||
RC suffixes can also be followed by a number.
|
||||
of `-patch`, `-alpha`, `-beta` or `-RC`. The suffixes can also be followed by
|
||||
a number.
|
||||
|
||||
Here are a few examples of valid tag names:
|
||||
|
||||
|
@ -82,6 +82,10 @@ Here are a few examples of valid tag names:
|
|||
v2.0.0-alpha
|
||||
v2.0.4-p1
|
||||
|
||||
> **Note:** Even if your tag is prefixed with `v`, a [version constraint](01-basic-usage.md#package-versions)
|
||||
> in a `require` statement has to be specified without prefix
|
||||
> (e.g. tag `v1.0.0` will result in version `1.0.0`).
|
||||
|
||||
### Branches
|
||||
|
||||
For every branch, a package development version will be created. If the branch
|
||||
|
@ -98,7 +102,9 @@ Here are some examples of version branch names:
|
|||
1.0 (equals 1.0.x)
|
||||
1.1.x
|
||||
|
||||
> **Note:** When you install a dev version, it will install it from source.
|
||||
> **Note:** When you install a development version, it will be automatically
|
||||
> pulled from its `source`. See the [`install`](03-cli.md#install) command
|
||||
> for more details.
|
||||
|
||||
### Aliases
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ resolution.
|
|||
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
||||
* **--no-dev:** Skip installing packages listed in `require-dev`.
|
||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||
* **--no-custom-installers:** Disables custom installers.
|
||||
* **--no-plugins:** Disables plugins.
|
||||
* **--no-progress:** Removes the progress display that can mess with some
|
||||
terminals or scripts which don't handle backspace characters.
|
||||
* **--optimize-autoloader (-o):** Convert PSR-0 autoloading to classmap to get a faster
|
||||
|
@ -115,14 +115,16 @@ You can also use wildcards to update a bunch of packages at once:
|
|||
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
||||
* **--no-dev:** Skip installing packages listed in `require-dev`.
|
||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||
* **--no-custom-installers:** Disables custom installers.
|
||||
* **--no-plugins:** Disables plugins.
|
||||
* **--no-progress:** Removes the progress display that can mess with some
|
||||
terminals or scripts which don't handle backspace characters.
|
||||
* **--optimize-autoloader (-o):** Convert PSR-0 autoloading to classmap to get a faster
|
||||
autoloader. This is recommended especially for production, but can take
|
||||
a bit of time to run so it is currently not done by default.
|
||||
* **--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.
|
||||
So all packages with their dependencies are updated recursively.
|
||||
|
||||
## require
|
||||
|
||||
|
@ -151,7 +153,7 @@ to the command.
|
|||
## global
|
||||
|
||||
The global command allows you to run other commands like `install`, `require`
|
||||
or `update` as if you were running them from the [COMPOSER_HOME](#COMPOSER_HOME)
|
||||
or `update` as if you were running them from the [COMPOSER_HOME](#composer-home)
|
||||
directory.
|
||||
|
||||
This can be used to install CLI utilities globally and if you add
|
||||
|
@ -267,11 +269,20 @@ command. It will replace your `composer.phar` with the latest version.
|
|||
|
||||
$ php composer.phar self-update
|
||||
|
||||
If you would like to instead update to a specific release simply specify it:
|
||||
|
||||
$ composer self-update 1.0.0-alpha7
|
||||
|
||||
If you have installed composer for your entire system (see [global installation](00-intro.md#globally)),
|
||||
you have to run the command with `root` privileges
|
||||
you may have to run the command with `root` privileges
|
||||
|
||||
$ sudo composer self-update
|
||||
|
||||
### Options
|
||||
|
||||
* **--rollback (-r):** Rollback to the last version you had installed.
|
||||
* **--clean-backups:** Delete old backups during an update. This makes the current version of composer the only backup available after the update.
|
||||
|
||||
## config
|
||||
|
||||
The `config` command allows you to edit some basic composer settings in either
|
||||
|
@ -330,7 +341,7 @@ provide a version as third argument, otherwise the latest version is used.
|
|||
|
||||
If the directory does not currently exist, it will be created during installation.
|
||||
|
||||
php composer.phar create-project doctrine/orm path 2.2.0
|
||||
php composer.phar create-project doctrine/orm path 2.2.*
|
||||
|
||||
It is also possible to run the command without params in a directory with an
|
||||
existing `composer.json` file to bootstrap a project.
|
||||
|
@ -346,7 +357,8 @@ By default the command checks for the packages on packagist.org.
|
|||
* **--prefer-source:** Install packages from `source` when available.
|
||||
* **--prefer-dist:** Install packages from `dist` when available.
|
||||
* **--dev:** Install packages listed in `require-dev`.
|
||||
* **--no-custom-installers:** Disables custom installers.
|
||||
* **--no-install:** Disables installation of the vendors.
|
||||
* **--no-plugins:** Disables plugins.
|
||||
* **--no-scripts:** Disables the execution of the scripts defined in the root
|
||||
package.
|
||||
* **--no-progress:** Removes the progress display that can mess with some
|
||||
|
@ -392,6 +404,20 @@ problems.
|
|||
|
||||
$ php composer.phar diagnose
|
||||
|
||||
## archive
|
||||
|
||||
This command is used to generate a zip/tar archive for a given package in a
|
||||
given version. It can also be used to archive your entire project without
|
||||
excluded/ignored files.
|
||||
|
||||
$ php composer.phar archive vendor/package 2.0.21 --format=zip
|
||||
|
||||
### Options
|
||||
|
||||
* **--format (-f):** Format of the resulting archive: tar or zip (default:
|
||||
"tar")
|
||||
* **--dir:** Write the archive to this directory (default: ".")
|
||||
|
||||
## help
|
||||
|
||||
To get more information about a certain command, just use `help`.
|
||||
|
|
|
@ -86,7 +86,7 @@ that needs some special logic, you can define a custom type. This could be a
|
|||
all be specific to certain projects, and they will need to provide an
|
||||
installer capable of installing packages of that type.
|
||||
|
||||
Out of the box, composer supports three types:
|
||||
Out of the box, composer supports four types:
|
||||
|
||||
- **library:** This is the default. It will simply copy the files to `vendor`.
|
||||
- **project:** This denotes a project rather than a library. For example
|
||||
|
@ -99,7 +99,7 @@ Out of the box, composer supports three types:
|
|||
their installation, but contains no files and will not write anything to the
|
||||
filesystem. As such, it does not require a dist or source key to be
|
||||
installable.
|
||||
- **composer-installer:** A package of type `composer-installer` provides an
|
||||
- **composer-plugin:** A package of type `composer-plugin` may provide an
|
||||
installer for other packages that have a custom type. Read more in the
|
||||
[dedicated article](articles/custom-installers.md).
|
||||
|
||||
|
@ -263,7 +263,7 @@ All links are optional fields.
|
|||
These allow you to further restrict or expand the stability of a package beyond
|
||||
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
|
||||
them to a constraint, or just apply them to an empty constraint if you want to
|
||||
allow unstable packages of a dependency's dependency for example.
|
||||
allow unstable packages of a dependency for example.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -274,13 +274,22 @@ Example:
|
|||
}
|
||||
}
|
||||
|
||||
If one of your dependencies has a dependency on an unstable package you need to
|
||||
explicitly require it as well, along with its sufficient stability flag.
|
||||
|
||||
Example:
|
||||
|
||||
{
|
||||
"require": {
|
||||
"doctrine/doctrine-fixtures-bundle": "dev-master",
|
||||
"doctrine/data-fixtures": "@dev"
|
||||
}
|
||||
}
|
||||
|
||||
`require` and `require-dev` additionally support explicit references (i.e.
|
||||
commit) for dev versions to make sure they are locked to a given state, even
|
||||
when you run update. These only work if you explicitly require a dev version
|
||||
and append the reference with `#<ref>`. Note that while this is convenient at
|
||||
times, it should not really be how you use packages in the long term. You
|
||||
should always try to switch to tagged releases as soon as you can, especially
|
||||
if the project you work on will not be touched for a while.
|
||||
and append the reference with `#<ref>`.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -291,8 +300,15 @@ Example:
|
|||
}
|
||||
}
|
||||
|
||||
It is possible to inline-alias a package constraint so that it matches a
|
||||
constraint that it otherwise would not. For more information [see the
|
||||
> **Note:** While this is convenient at times, it should not be how you use
|
||||
> packages in the long term because it comes with a technical limitation. The
|
||||
> composer.json metadata will still be read from the branch name you specify
|
||||
> before the hash. Because of that in some cases it will not be a practical
|
||||
> workaround, and you should always try to switch to tagged releases as soon
|
||||
> as you can.
|
||||
|
||||
It is also possible to inline-alias a package constraint so that it matches
|
||||
a constraint that it otherwise would not. For more information [see the
|
||||
aliases article](articles/aliases.md).
|
||||
|
||||
#### require
|
||||
|
@ -640,6 +656,13 @@ The following options are supported:
|
|||
dist (zip, tar, ..) packages that it downloads. When the garbage collection
|
||||
is periodically ran, this is the maximum size the cache will be able to use.
|
||||
Older (less used) files will be removed first until the cache fits.
|
||||
* **prepend-autoloader:** Defaults to `true`. If false, the composer autoloader
|
||||
will not be prepended to existing autoloaders. This is sometimes required to fix
|
||||
interoperability issues with other autoloaders.
|
||||
* **autoloader-suffix:** Defaults to `null`. String to be used as a suffix for
|
||||
the generated Composer autoloader. When null a random one will be generated.
|
||||
* **github-domains:** Defaults to `["github.com"]`. A list of domains to use in
|
||||
github mode. This is used for GitHub Enterprise setups.
|
||||
* **notify-on-install:** Defaults to `true`. Composer allows repositories to
|
||||
define a notification URL, so that they get notified whenever a package from
|
||||
that repository is installed. This option allows you to disable that behaviour.
|
||||
|
|
|
@ -463,8 +463,8 @@ there are some use cases for hosting your own repository.
|
|||
might want to keep them separate to packagist. An example of this would be
|
||||
wordpress plugins.
|
||||
|
||||
When hosting your own package repository it is recommended to use a `composer`
|
||||
one. This is type that is native to composer and yields the best performance.
|
||||
For hosting your own packages, a native `composer` type of repository is
|
||||
recommended, which provides the best performance.
|
||||
|
||||
There are a few tools that can help you create a `composer` repository.
|
||||
|
||||
|
@ -523,7 +523,7 @@ private packages:
|
|||
|
||||
Each zip artifact is just a ZIP archive with `composer.json` in root folder:
|
||||
|
||||
$ tar -tf acme-corp-parser-10.3.5.zip
|
||||
$ unzip -l acme-corp-parser-10.3.5.zip
|
||||
composer.json
|
||||
...
|
||||
|
||||
|
|
|
@ -59,8 +59,9 @@ is a dependency of your local project.
|
|||
|
||||
For this reason, you can alias packages in your `require` and `require-dev`
|
||||
fields. Let's say you found a bug in the `monolog/monolog` package. You cloned
|
||||
Monolog on GitHub and fixed the issue in a branch named `bugfix`. Now you want
|
||||
to install that version of monolog in your local project.
|
||||
[Monolog](https://github.com/Seldaek/monolog) on GitHub and fixed the issue in
|
||||
a branch named `bugfix`. Now you want to install that version of monolog in your
|
||||
local project.
|
||||
|
||||
You are using `symfony/monolog-bundle` which requires `monolog/monolog` version
|
||||
`1.*`. So you need your `dev-bugfix` to match that constraint.
|
||||
|
|
|
@ -29,8 +29,8 @@ An example use-case would be:
|
|||
|
||||
> phpDocumentor features Templates that need to be installed outside of the
|
||||
> default /vendor folder structure. As such they have chosen to adopt the
|
||||
> `phpdocumentor-template` [type][1] and create a Custom Installer to send
|
||||
> these templates to the correct folder.
|
||||
> `phpdocumentor-template` [type][1] and create a plugin providing the Custom
|
||||
> Installer to send these templates to the correct folder.
|
||||
|
||||
An example composer.json of such a template package would be:
|
||||
|
||||
|
@ -38,59 +38,85 @@ An example composer.json of such a template package would be:
|
|||
"name": "phpdocumentor/template-responsive",
|
||||
"type": "phpdocumentor-template",
|
||||
"require": {
|
||||
"phpdocumentor/template-installer": "*"
|
||||
"phpdocumentor/template-installer-plugin": "*"
|
||||
}
|
||||
}
|
||||
|
||||
> **IMPORTANT**: to make sure that the template installer is present at the
|
||||
> time the template package is installed, template packages should require
|
||||
> the installer package.
|
||||
> the plugin package.
|
||||
|
||||
## Creating an Installer
|
||||
|
||||
A Custom Installer is defined as a class that implements the
|
||||
[`Composer\Installer\InstallerInterface`][3] and is contained in a Composer
|
||||
package that has the [type][1] `composer-installer`.
|
||||
[`Composer\Installer\InstallerInterface`][3] and is usually distributed in a
|
||||
Composer Plugin.
|
||||
|
||||
A basic Installer would thus compose of two files:
|
||||
A basic Installer Plugin would thus compose of three files:
|
||||
|
||||
1. the package file: composer.json
|
||||
2. The Installer class, e.g.: `My\Project\Composer\Installer.php`, containing a class that implements `Composer\Installer\InstallerInterface`.
|
||||
2. The Plugin class, e.g.: `My\Project\Composer\Plugin.php`, containing a class that implements `Composer\Plugin\PluginInterface`.
|
||||
3. The Installer class, e.g.: `My\Project\Composer\Installer.php`, containing a class that implements `Composer\Installer\InstallerInterface`.
|
||||
|
||||
### composer.json
|
||||
|
||||
The package file is the same as any other package file but with the following
|
||||
requirements:
|
||||
|
||||
1. the [type][1] attribute must be `composer-installer`.
|
||||
1. the [type][1] attribute must be `composer-plugin`.
|
||||
2. the [extra][2] attribute must contain an element `class` defining the
|
||||
class name of the installer (including namespace). If a package contains
|
||||
multiple installers this can be array of class names.
|
||||
class name of the plugin (including namespace). If a package contains
|
||||
multiple plugins this can be array of class names.
|
||||
|
||||
Example:
|
||||
|
||||
{
|
||||
"name": "phpdocumentor/template-installer",
|
||||
"type": "composer-installer",
|
||||
"name": "phpdocumentor/template-installer-plugin",
|
||||
"type": "composer-plugin",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-0": {"phpDocumentor\\Composer": "src/"}
|
||||
},
|
||||
"extra": {
|
||||
"class": "phpDocumentor\\Composer\\TemplateInstaller"
|
||||
"class": "phpDocumentor\\Composer\\TemplateInstallerPlugin"
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
### The Plugin class
|
||||
|
||||
The class defining the Composer plugin must implement the
|
||||
[`Composer\Plugin\PluginInterface`][3]. It can then register the Custom
|
||||
Installer in its `activate()` method.
|
||||
|
||||
The class may be placed in any location and have any name, as long as it is
|
||||
autoloadable and matches the `extra.class` element in the package definition.
|
||||
|
||||
Example:
|
||||
|
||||
namespace phpDocumentor\Composer;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
|
||||
class TemplateInstallerPlugin implements PluginInterface
|
||||
{
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$installer = new TemplateInstaller($io, $composer);
|
||||
$composer->getInstallationManager()->addInstaller($installer);
|
||||
}
|
||||
}
|
||||
|
||||
### The Custom Installer class
|
||||
|
||||
The class that executes the custom installation should implement the
|
||||
[`Composer\Installer\InstallerInterface`][3] (or extend another installer that
|
||||
implements that interface).
|
||||
|
||||
The class may be placed in any location and have any name, as long as it is
|
||||
autoloadable and matches the `extra.class` element in the package definition.
|
||||
It will also define the [type][1] string as it will be recognized by packages
|
||||
that will use this installer in the `supports()` method.
|
||||
[`Composer\Installer\InstallerInterface`][4] (or extend another installer that
|
||||
implements that interface). It defines the [type][1] string as it will be
|
||||
recognized by packages that will use this installer in the `supports()` method.
|
||||
|
||||
> **NOTE**: _choose your [type][1] name carefully, it is recommended to follow
|
||||
> the format: `vendor-type`_. For example: `phpdocumentor-template`.
|
||||
|
@ -122,7 +148,7 @@ Example:
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getInstallPath(PackageInterface $package)
|
||||
public function getPackageBasePath(PackageInterface $package)
|
||||
{
|
||||
$prefix = substr($package->getPrettyName(), 0, 23);
|
||||
if ('phpdocumentor/template-' !== $prefix) {
|
||||
|
@ -146,7 +172,7 @@ Example:
|
|||
}
|
||||
|
||||
The example demonstrates that it is quite simple to extend the
|
||||
[`Composer\Installer\LibraryInstaller`][4] class to strip a prefix
|
||||
[`Composer\Installer\LibraryInstaller`][5] class to strip a prefix
|
||||
(`phpdocumentor/template-`) and use the remaining part to assemble a completely
|
||||
different installation path.
|
||||
|
||||
|
@ -155,5 +181,6 @@ different installation path.
|
|||
|
||||
[1]: ../04-schema.md#type
|
||||
[2]: ../04-schema.md#extra
|
||||
[3]: https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerInterface.php
|
||||
[4]: https://github.com/composer/composer/blob/master/src/Composer/Installer/LibraryInstaller.php
|
||||
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
|
||||
[4]: https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerInterface.php
|
||||
[5]: https://github.com/composer/composer/blob/master/src/Composer/Installer/LibraryInstaller.php
|
||||
|
|
|
@ -124,7 +124,7 @@ Example using HTTP over SSL using a client certificate:
|
|||
"url": "https://example.org",
|
||||
"options": {
|
||||
"ssl": {
|
||||
"local_cert": "/home/composer/.ssl/composer.pem",
|
||||
"local_cert": "/home/composer/.ssl/composer.pem"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,3 +170,19 @@ bucket or on a CDN host. A CDN would drastically improve download times and ther
|
|||
|
||||
Example: A `prefix-url` of `http://my-bucket.s3.amazonaws.com` (and `directory` set to `dist`) creates download URLs
|
||||
which look like the following: `http://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`.
|
||||
|
||||
|
||||
### Resolving dependencies
|
||||
|
||||
It is possible to make satis automatically resolve and add all dependencies for your projects. This can be used
|
||||
with the Downloads functionality to have a complete local mirror of packages. Just add the following
|
||||
to your `satis.json`:
|
||||
|
||||
```
|
||||
{
|
||||
"require-dependencies": true
|
||||
}
|
||||
```
|
||||
|
||||
When searching for packages, satis will attempt to resolve all the required packages from the listed repositories.
|
||||
Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`.
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<!--
|
||||
tagline: Modify and extend Composer's functionality
|
||||
-->
|
||||
|
||||
# Setting up and using plugins
|
||||
|
||||
## Synopsis
|
||||
|
||||
You may wish to alter or expand Composer's functionality with your own. For
|
||||
example if your environment poses special requirements on the behaviour of
|
||||
Composer which do not apply to the majority of its users or if you wish to
|
||||
accomplish something with composer in a way that is not desired by most users.
|
||||
|
||||
In these cases you could consider creating a plugin to handle your
|
||||
specific logic.
|
||||
|
||||
## Creating a Plugin
|
||||
|
||||
A plugin is a regular composer package which ships its code as part of the
|
||||
package and may also depend on further packages.
|
||||
|
||||
### Plugin Package
|
||||
|
||||
The package file is the same as any other package file but with the following
|
||||
requirements:
|
||||
|
||||
1. the [type][1] attribute must be `composer-plugin`.
|
||||
2. the [extra][2] attribute must contain an element `class` defining the
|
||||
class name of the plugin (including namespace). If a package contains
|
||||
multiple plugins this can be array of class names.
|
||||
|
||||
Additionally you must require the special package called `composer-plugin-api`
|
||||
to define which composer API versions your plugin is compatible with. The
|
||||
current composer plugin API version is 1.0.0.
|
||||
|
||||
For example
|
||||
|
||||
{
|
||||
"name": "my/plugin-package",
|
||||
"type": "composer-plugin",
|
||||
"require": {
|
||||
"composer-plugin-api": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
### Plugin Class
|
||||
|
||||
Every plugin has to supply a class which implements the
|
||||
[`Composer\Plugin\PluginInterface`][3]. The `activate()` method of the plugin
|
||||
is called after the plugin is loaded and receives an instance of
|
||||
[`Composer\Composer`][4] as well as an instance of
|
||||
[`Composer\IO\IOInterface`][5]. Using these two objects all configuration can
|
||||
be read and all internal objects and state can be manipulated as desired.
|
||||
|
||||
Example:
|
||||
|
||||
namespace phpDocumentor\Composer;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
|
||||
class TemplateInstallerPlugin implements PluginInterface
|
||||
{
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$installer = new TemplateInstaller($io, $composer);
|
||||
$composer->getInstallationManager()->addInstaller($installer);
|
||||
}
|
||||
}
|
||||
|
||||
## Event Handler
|
||||
|
||||
Furthermore plugins may implement the
|
||||
[`Composer\EventDispatcher\EventSubscriberInterface`][6] in order to have its
|
||||
event handlers automatically registered with the `EventDispatcher` when the
|
||||
plugin is loaded.
|
||||
|
||||
The events available for plugins are:
|
||||
|
||||
* **COMMAND**, is called at the beginning of all commands that load plugins.
|
||||
It provides you with access to the input and output objects of the program.
|
||||
* **PRE_FILE_DOWNLOAD**, is triggered before files are downloaded and allows
|
||||
you to manipulate the `RemoteFilesystem` object prior to downloading files
|
||||
based on the URL to be downloaded.
|
||||
|
||||
> A plugin can also subscribe to [script events][7].
|
||||
|
||||
Example:
|
||||
|
||||
namespace Naderman\Composer\AWS;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PreFileDownloadEvent;
|
||||
|
||||
class AwsPlugin implements PluginInterface, EventSubscriberInterface
|
||||
{
|
||||
protected $composer;
|
||||
protected $io;
|
||||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
PluginEvents::PRE_FILE_DOWNLOAD => array(
|
||||
array('onPreFileDownload', 0)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function onPreFileDownload(PreFileDownloadEvent $event)
|
||||
{
|
||||
$protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME);
|
||||
|
||||
if ($protocol === 's3') {
|
||||
$awsClient = new AwsClient($this->io, $this->composer->getConfig());
|
||||
$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
|
||||
$event->setRemoteFilesystem($s3RemoteFilesystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## Using Plugins
|
||||
|
||||
Plugin packages are automatically loaded as soon as they are installed and will
|
||||
be loaded when composer starts up if they are found in the current project's
|
||||
list of installed packages. Additionally all plugin packages installed in the
|
||||
`COMPOSER_HOME` directory using the composer global command are loaded before
|
||||
local project plugins are loaded.
|
||||
|
||||
> You may pass the `--no-plugins` option to composer commands to disable all
|
||||
> installed commands. This may be particularly helpful if any of the plugins
|
||||
> causes errors and you wish to update or uninstall it.
|
||||
|
||||
[1]: ../04-schema.md#type
|
||||
[2]: ../04-schema.md#extra
|
||||
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
|
||||
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
|
||||
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
|
||||
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
|
||||
[7]: ./scripts.md#event-names
|
|
@ -41,6 +41,13 @@ Composer fires the following named events during its execution process:
|
|||
- **post-create-project-cmd**: occurs after the `create-project` command is
|
||||
executed.
|
||||
|
||||
**NOTE: Composer makes no assumptions about the state of your dependencies
|
||||
prior to `install` or `update`. Therefore, you should not specify scripts that
|
||||
require Composer-managed dependencies in the `pre-update-cmd` or
|
||||
`pre-install-cmd` event hooks. If you need to execute scripts prior to
|
||||
`install` or `update` please make sure they are self-contained within your
|
||||
root package.**
|
||||
|
||||
## Defining scripts
|
||||
|
||||
The root JSON object in `composer.json` should have a property called
|
||||
|
@ -108,3 +115,11 @@ PHP callback. This `Event` object has getters for other contextual objects:
|
|||
- `getName()`: returns the name of the event being fired as a string
|
||||
- `getIO()`: returns the current input/output stream which implements
|
||||
`Composer\IO\IOInterface` for writing to the console
|
||||
|
||||
## Running scripts manually
|
||||
|
||||
If you would like to run the scripts for an event manually, the syntax is:
|
||||
|
||||
$ composer run-script [--dev] [--no-dev] script
|
||||
|
||||
For example `composer run-script post-install-cmd` will run any **post-install-cmd** scripts that have been defined.
|
||||
|
|
|
@ -58,7 +58,7 @@ Say project `my-vendor/project-b` has requirements setup like this:
|
|||
|
||||
{
|
||||
"name": "my-vendor/project-b",
|
||||
"requires": {
|
||||
"require": {
|
||||
"my-vendor/project-a": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"description": "Package type, either 'library' for common packages, 'composer-installer' for custom installers, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.",
|
||||
"description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.",
|
||||
"type": "string"
|
||||
},
|
||||
"target-dir": {
|
||||
|
@ -175,12 +175,27 @@
|
|||
"discard-changes": {
|
||||
"type": ["string", "boolean"],
|
||||
"description": "The default style of handling dirty updates, defaults to false and can be any of true, false or \"stash\"."
|
||||
},
|
||||
"autoloader-suffix": {
|
||||
"type": "string",
|
||||
"description": "Optional string to be used as a suffix for the generated Composer autoloader. When null a random one will be generated."
|
||||
},
|
||||
"prepend-autoloader": {
|
||||
"type": "boolean",
|
||||
"description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true."
|
||||
},
|
||||
"github-domains": {
|
||||
"type": "array",
|
||||
"description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"type": ["object", "array"],
|
||||
"description": "Arbitrary extra data that can be used by custom installers, for example, package of type composer-installer must have a 'class' key defining the installer class name.",
|
||||
"description": "Arbitrary extra data that can be used by plugins, for example, package of type composer-plugin may have a 'class' key defining an installer class name.",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
namespace Composer\Autoload;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Installer\InstallationManager;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Script\EventDispatcher;
|
||||
use Composer\Script\ScriptEvents;
|
||||
|
||||
/**
|
||||
|
@ -39,13 +39,14 @@ class AutoloadGenerator
|
|||
|
||||
public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
|
||||
{
|
||||
$this->eventDispatcher->dispatch(ScriptEvents::PRE_AUTOLOAD_DUMP);
|
||||
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP);
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
$filesystem->ensureDirectoryExists($config->get('vendor-dir'));
|
||||
$basePath = $filesystem->normalizePath(getcwd());
|
||||
$basePath = $filesystem->normalizePath(realpath(getcwd()));
|
||||
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
|
||||
$useGlobalIncludePath = (bool) $config->get('use-include-path');
|
||||
$prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
|
||||
$targetDir = $vendorPath.'/'.$targetDir;
|
||||
$filesystem->ensureDirectoryExists($targetDir);
|
||||
|
||||
|
@ -59,7 +60,7 @@ class AutoloadGenerator
|
|||
$namespacesFile = <<<EOF
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php generated by Composer
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
\$vendorDir = $vendorPathCode52;
|
||||
\$baseDir = $appBaseDirCode;
|
||||
|
@ -85,7 +86,7 @@ EOF;
|
|||
$classmapFile = <<<EOF
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php generated by Composer
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
\$vendorDir = $vendorPathCode52;
|
||||
\$baseDir = $appBaseDirCode;
|
||||
|
@ -168,7 +169,7 @@ EOF;
|
|||
$classmapFile .= ");\n";
|
||||
|
||||
if (!$suffix) {
|
||||
$suffix = md5(uniqid('', true));
|
||||
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
|
||||
}
|
||||
|
||||
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
|
||||
|
@ -180,7 +181,7 @@ EOF;
|
|||
file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
|
||||
}
|
||||
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
|
||||
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath));
|
||||
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader));
|
||||
|
||||
// use stream_copy_to_stream instead of copy
|
||||
// to work around https://bugs.php.net/bug.php?id=64634
|
||||
|
@ -191,7 +192,7 @@ EOF;
|
|||
fclose($targetLoader);
|
||||
unset($sourceLoader, $targetLoader);
|
||||
|
||||
$this->eventDispatcher->dispatch(ScriptEvents::POST_AUTOLOAD_DUMP);
|
||||
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP);
|
||||
}
|
||||
|
||||
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
|
||||
|
@ -284,7 +285,7 @@ EOF;
|
|||
return <<<EOF
|
||||
<?php
|
||||
|
||||
// include_paths.php generated by Composer
|
||||
// include_paths.php @generated by Composer
|
||||
|
||||
\$vendorDir = $vendorPathCode;
|
||||
\$baseDir = $appBaseDirCode;
|
||||
|
@ -310,13 +311,14 @@ EOF;
|
|||
return <<<EOF
|
||||
<?php
|
||||
|
||||
// autoload_files.php generated by Composer
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
\$vendorDir = $vendorPathCode;
|
||||
\$baseDir = $appBaseDirCode;
|
||||
|
||||
return array(
|
||||
$filesCode);
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
|
@ -328,7 +330,7 @@ EOF;
|
|||
$path = $filesystem->normalizePath($path);
|
||||
|
||||
$baseDir = '';
|
||||
if (strpos($path, $vendorPath) === 0) {
|
||||
if (strpos($path.'/', $vendorPath.'/') === 0) {
|
||||
$path = substr($path, strlen($vendorPath));
|
||||
$baseDir = '$vendorDir';
|
||||
|
||||
|
@ -355,7 +357,7 @@ EOF;
|
|||
return <<<AUTOLOAD
|
||||
<?php
|
||||
|
||||
// autoload.php generated by Composer
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once $vendorPathToTargetDirCode . '/autoload_real.php';
|
||||
|
||||
|
@ -364,7 +366,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
|
|||
AUTOLOAD;
|
||||
}
|
||||
|
||||
protected function getAutoloadRealFile($usePSR0, $useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath)
|
||||
protected function getAutoloadRealFile($usePSR0, $useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
|
||||
{
|
||||
// TODO the class ComposerAutoloaderInit should be revert to a closure
|
||||
// when APC has been fixed:
|
||||
|
@ -376,7 +378,7 @@ AUTOLOAD;
|
|||
$file = <<<HEADER
|
||||
<?php
|
||||
|
||||
// autoload_real.php generated by Composer
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit$suffix
|
||||
{
|
||||
|
@ -395,7 +397,7 @@ class ComposerAutoloaderInit$suffix
|
|||
return self::\$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'), true, $prependAutoloader);
|
||||
self::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));
|
||||
|
||||
|
@ -454,15 +456,16 @@ REGISTER_AUTOLOAD;
|
|||
}
|
||||
|
||||
$file .= <<<REGISTER_LOADER
|
||||
\$loader->register(true);
|
||||
\$loader->register($prependAutoloader);
|
||||
|
||||
|
||||
REGISTER_LOADER;
|
||||
|
||||
if ($useIncludeFiles) {
|
||||
$file .= <<<INCLUDE_FILES
|
||||
foreach (require __DIR__ . '/autoload_files.php' as \$file) {
|
||||
require \$file;
|
||||
$file .= <<<'INCLUDE_FILES'
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
foreach ($includeFiles as $file) {
|
||||
require $file;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ use Symfony\Component\Finder\Finder;
|
|||
*/
|
||||
class Cache
|
||||
{
|
||||
private static $cacheCollected = false;
|
||||
private $io;
|
||||
private $root;
|
||||
private $enabled = true;
|
||||
|
@ -126,6 +127,11 @@ class Cache
|
|||
return false;
|
||||
}
|
||||
|
||||
public function gcIsNecessary()
|
||||
{
|
||||
return (!self::$cacheCollected && !mt_rand(0, 50));
|
||||
}
|
||||
|
||||
public function remove($file)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
|
@ -157,6 +163,8 @@ class Cache
|
|||
}
|
||||
}
|
||||
|
||||
self::$cacheCollected = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,16 +38,17 @@ abstract class Command extends BaseCommand
|
|||
|
||||
/**
|
||||
* @param bool $required
|
||||
* @param bool $disablePlugins
|
||||
* @throws \RuntimeException
|
||||
* @return Composer
|
||||
*/
|
||||
public function getComposer($required = true)
|
||||
public function getComposer($required = true, $disablePlugins = false)
|
||||
{
|
||||
if (null === $this->composer) {
|
||||
$application = $this->getApplication();
|
||||
if ($application instanceof Application) {
|
||||
/* @var $application Application */
|
||||
$this->composer = $application->getComposer($required);
|
||||
$this->composer = $application->getComposer($required, $disablePlugins);
|
||||
} elseif ($required) {
|
||||
throw new \RuntimeException(
|
||||
'Could not create a Composer\Composer instance, you must inject '.
|
||||
|
|
|
@ -117,7 +117,7 @@ EOT
|
|||
if ($input->getOption('global') && !$this->configFile->exists()) {
|
||||
touch($this->configFile->getPath());
|
||||
$this->configFile->write(array('config' => new \ArrayObject));
|
||||
chmod($this->configFile->getPath(), 0600);
|
||||
@chmod($this->configFile->getPath(), 0600);
|
||||
}
|
||||
|
||||
if (!$this->configFile->exists()) {
|
||||
|
@ -254,18 +254,12 @@ EOT
|
|||
// handle config values
|
||||
$uniqueConfigValues = array(
|
||||
'process-timeout' => array('is_numeric', 'intval'),
|
||||
'use-include-path' => array(
|
||||
$booleanValidator,
|
||||
$booleanNormalizer
|
||||
),
|
||||
'use-include-path' => array($booleanValidator, $booleanNormalizer),
|
||||
'preferred-install' => array(
|
||||
function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); },
|
||||
function ($val) { return $val; }
|
||||
),
|
||||
'notify-on-install' => array(
|
||||
$booleanValidator,
|
||||
$booleanNormalizer
|
||||
),
|
||||
'notify-on-install' => array($booleanValidator, $booleanNormalizer),
|
||||
'vendor-dir' => array('is_string', function ($val) { return $val; }),
|
||||
'bin-dir' => array('is_string', function ($val) { return $val; }),
|
||||
'cache-dir' => array('is_string', function ($val) { return $val; }),
|
||||
|
@ -288,6 +282,8 @@ EOT
|
|||
return $val !== 'false' && (bool) $val;
|
||||
}
|
||||
),
|
||||
'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
|
||||
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
|
||||
);
|
||||
$multiConfigValues = array(
|
||||
'github-protocols' => array(
|
||||
|
@ -308,6 +304,18 @@ EOT
|
|||
return $vals;
|
||||
}
|
||||
),
|
||||
'github-domains' => array(
|
||||
function ($vals) {
|
||||
if (!is_array($vals)) {
|
||||
return 'array expected';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
function ($vals) {
|
||||
return $vals;
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($uniqueConfigValues as $name => $callbacks) {
|
||||
|
|
|
@ -44,6 +44,7 @@ use Composer\Package\Version\VersionParser;
|
|||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Tobias Munk <schmunk@usrbin.de>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class CreateProjectCommand extends Command
|
||||
{
|
||||
|
@ -56,16 +57,18 @@ class CreateProjectCommand extends Command
|
|||
new InputArgument('package', InputArgument::OPTIONAL, 'Package name to be installed'),
|
||||
new InputArgument('directory', InputArgument::OPTIONAL, 'Directory where the files should be created'),
|
||||
new InputArgument('version', InputArgument::OPTIONAL, 'Version, will defaults to latest'),
|
||||
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).', 'stable'),
|
||||
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'),
|
||||
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
|
||||
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
|
||||
new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'),
|
||||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'Whether to disable custom installers.'),
|
||||
new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Whether to disable plugins.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
|
||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Whether to prevent execution of all defined scripts in the root package.'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deletion vcs folder.'),
|
||||
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The <info>create-project</info> command creates a new project from a given
|
||||
|
@ -116,6 +119,11 @@ EOT
|
|||
$preferDist = $input->getOption('prefer-dist');
|
||||
}
|
||||
|
||||
if ($input->getOption('no-custom-installers')) {
|
||||
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||
$input->setOption('no-plugins', true);
|
||||
}
|
||||
|
||||
return $this->installProject(
|
||||
$this->getIO(),
|
||||
$config,
|
||||
|
@ -127,24 +135,26 @@ EOT
|
|||
$preferDist,
|
||||
!$input->getOption('no-dev'),
|
||||
$input->getOption('repository-url'),
|
||||
$input->getOption('no-custom-installers'),
|
||||
$input->getOption('no-plugins'),
|
||||
$input->getOption('no-scripts'),
|
||||
$input->getOption('keep-vcs'),
|
||||
$input->getOption('no-progress')
|
||||
$input->getOption('no-progress'),
|
||||
$input->getOption('no-install')
|
||||
);
|
||||
}
|
||||
|
||||
public function installProject(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disableCustomInstallers = false, $noScripts = false, $keepVcs = false, $noProgress = false)
|
||||
public function installProject(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false)
|
||||
{
|
||||
$oldCwd = getcwd();
|
||||
|
||||
if ($packageName !== null) {
|
||||
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositoryUrl, $disableCustomInstallers, $noScripts, $keepVcs, $noProgress);
|
||||
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositoryUrl, $disablePlugins, $noScripts, $keepVcs, $noProgress);
|
||||
} else {
|
||||
$installedFromVcs = false;
|
||||
}
|
||||
|
||||
$composer = Factory::create($io);
|
||||
$composer = Factory::create($io, null, $disablePlugins);
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($noScripts === false) {
|
||||
// dispatch event
|
||||
|
@ -152,18 +162,21 @@ EOT
|
|||
}
|
||||
|
||||
// install dependencies of the created project
|
||||
$installer = Installer::create($io, $composer);
|
||||
$installer->setPreferSource($preferSource)
|
||||
->setPreferDist($preferDist)
|
||||
->setDevMode($installDevPackages)
|
||||
->setRunScripts( ! $noScripts);
|
||||
if ($noInstall === false) {
|
||||
$installer = Installer::create($io, $composer);
|
||||
$installer->setPreferSource($preferSource)
|
||||
->setPreferDist($preferDist)
|
||||
->setDevMode($installDevPackages)
|
||||
->setRunScripts( ! $noScripts);
|
||||
|
||||
if ($disableCustomInstallers) {
|
||||
$installer->disableCustomInstallers();
|
||||
}
|
||||
if ($disablePlugins) {
|
||||
$installer->disablePlugins();
|
||||
}
|
||||
|
||||
if (!$installer->run()) {
|
||||
return 1;
|
||||
$status = $installer->run();
|
||||
if (0 !== $status) {
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
||||
$hasVcs = $installedFromVcs;
|
||||
|
@ -180,7 +193,6 @@ EOT
|
|||
}
|
||||
|
||||
try {
|
||||
$fs = new Filesystem();
|
||||
$dirs = iterator_to_array($finder);
|
||||
unset($finder);
|
||||
foreach ($dirs as $dir) {
|
||||
|
@ -215,10 +227,10 @@ EOT
|
|||
|
||||
chdir($oldCwd);
|
||||
$vendorComposerDir = $composer->getConfig()->get('vendor-dir').'/composer';
|
||||
if (is_dir($vendorComposerDir) && glob($vendorComposerDir.'/*') === array() && count(glob($vendorComposerDir.'/.*')) === 2) {
|
||||
if (is_dir($vendorComposerDir) && $fs->isDirEmpty($vendorComposerDir)) {
|
||||
@rmdir($vendorComposerDir);
|
||||
$vendorDir = $composer->getConfig()->get('vendor-dir');
|
||||
if (is_dir($vendorDir) && glob($vendorDir.'/*') === array() && count(glob($vendorDir.'/.*')) === 2) {
|
||||
if (is_dir($vendorDir) && $fs->isDirEmpty($vendorDir)) {
|
||||
@rmdir($vendorDir);
|
||||
}
|
||||
}
|
||||
|
@ -226,16 +238,8 @@ EOT
|
|||
return 0;
|
||||
}
|
||||
|
||||
protected function installRootPackage(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disableCustomInstallers = false, $noScripts = false, $keepVcs = false, $noProgress = false)
|
||||
protected function installRootPackage(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false)
|
||||
{
|
||||
$stability = strtolower($stability);
|
||||
if ($stability === 'rc') {
|
||||
$stability = 'RC';
|
||||
}
|
||||
if (!isset(BasePackage::$stabilities[$stability])) {
|
||||
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
|
||||
}
|
||||
|
||||
if (null === $repositoryUrl) {
|
||||
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
|
||||
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) {
|
||||
|
@ -254,10 +258,24 @@ EOT
|
|||
$packageVersion = $requirements[0]['version'];
|
||||
}
|
||||
|
||||
$pool = new Pool($packageVersion ? 'dev' : $stability);
|
||||
if (null === $stability) {
|
||||
if (preg_match('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) {
|
||||
$stability = $match[1];
|
||||
} else {
|
||||
$stability = VersionParser::parseStability($packageVersion);
|
||||
}
|
||||
}
|
||||
|
||||
$stability = VersionParser::normalizeStability($stability);
|
||||
|
||||
if (!isset(BasePackage::$stabilities[$stability])) {
|
||||
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
|
||||
}
|
||||
|
||||
$pool = new Pool($stability);
|
||||
$pool->addRepository($sourceRepo);
|
||||
|
||||
$constraint = $packageVersion ? new VersionConstraint('=', $parser->normalize($packageVersion)) : null;
|
||||
$constraint = $packageVersion ? $parser->parseConstraints($packageVersion) : null;
|
||||
$candidates = $pool->whatProvides($name, $constraint);
|
||||
foreach ($candidates as $key => $candidate) {
|
||||
if ($candidate->getName() !== $name) {
|
||||
|
@ -275,7 +293,7 @@ EOT
|
|||
}
|
||||
|
||||
// select highest version if we have many
|
||||
$package = $candidates[0];
|
||||
$package = reset($candidates);
|
||||
foreach ($candidates as $candidate) {
|
||||
if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) {
|
||||
$package = $candidate;
|
||||
|
@ -285,8 +303,8 @@ EOT
|
|||
|
||||
$io->write('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
|
||||
|
||||
if ($disableCustomInstallers) {
|
||||
$io->write('<info>Custom installers have been disabled.</info>');
|
||||
if ($disablePlugins) {
|
||||
$io->write('<info>Plugins have been disabled.</info>');
|
||||
}
|
||||
|
||||
if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -50,7 +52,12 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$repo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
|
||||
$composer = $this->getComposer();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
$needle = $input->getArgument('package');
|
||||
|
||||
$pool = new Pool();
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Composer\Command;
|
|||
use Composer\Composer;
|
||||
use Composer\Factory;
|
||||
use Composer\Downloader\TransportException;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Util\ConfigValidator;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\StreamContextFactory;
|
||||
|
@ -64,6 +66,9 @@ EOT
|
|||
|
||||
$composer = $this->getComposer(false);
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$output->write('Checking composer.json: ');
|
||||
$this->outputResult($output, $this->checkComposerSchema());
|
||||
}
|
||||
|
@ -295,6 +300,12 @@ EOT
|
|||
$warnings['apc_cli'] = true;
|
||||
}
|
||||
|
||||
if (ini_get('xdebug.profiler_enabled')) {
|
||||
$warnings['xdebug_profile'] = true;
|
||||
} elseif (extension_loaded('xdebug')) {
|
||||
$warnings['xdebug_loaded'] = true;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
phpinfo(INFO_GENERAL);
|
||||
$phpinfo = ob_get_clean();
|
||||
|
@ -360,6 +371,18 @@ EOT
|
|||
$text = PHP_EOL."Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
|
||||
$text .= "Composer works with 5.3.2+ for most people, but there might be edge case issues.";
|
||||
break;
|
||||
|
||||
case 'xdebug_loaded':
|
||||
$text = PHP_EOL."The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
|
||||
$text .= "Disabling it when using Composer is recommended, but should not cause issues beyond slowness.";
|
||||
break;
|
||||
|
||||
case 'xdebug_profile':
|
||||
$text = PHP_EOL."The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL;
|
||||
$text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL;
|
||||
$text .= " xdebug.profiler_enabled = 0";
|
||||
$displayIniMessage = true;
|
||||
break;
|
||||
}
|
||||
$out($text, 'warning');
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -42,6 +44,10 @@ EOT
|
|||
$output->writeln('<info>Generating autoload files</info>');
|
||||
|
||||
$composer = $this->getComposer();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'dump-autoload', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$installationManager = $composer->getInstallationManager();
|
||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
$package = $composer->getPackage();
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\Installer;
|
||||
use Composer\Factory;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
@ -43,6 +42,9 @@ is to add the COMPOSER_HOME/vendor/bin dir to your PATH env var.
|
|||
COMPOSER_HOME is c:\Users\<user>\AppData\Roaming\Composer on Windows
|
||||
and /home/<user>/.composer on unix systems.
|
||||
|
||||
Note: This path may vary depending on customizations to bin-dir in
|
||||
composer.json or the environmental variable COMPOSER_BIN_DIR.
|
||||
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -70,10 +72,11 @@ EOT
|
|||
// change to global dir
|
||||
$config = Factory::createConfig();
|
||||
chdir($config->get('home'));
|
||||
$output->writeln('<info>Changed current directory to '.$config->get('home').'</info>');
|
||||
|
||||
// create new input without "global" command prefix
|
||||
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
|
||||
|
||||
return $this->getApplication()->get($args[1])->run($input, $output);
|
||||
return $this->getApplication()->run($input, $output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -208,7 +208,8 @@ EOT
|
|||
$description = $input->getOption('description') ?: false;
|
||||
$description = $dialog->ask(
|
||||
$output,
|
||||
$dialog->getQuestion('Description', $description)
|
||||
$dialog->getQuestion('Description', $description),
|
||||
$description
|
||||
);
|
||||
$input->setOption('description', $description);
|
||||
|
||||
|
@ -258,7 +259,8 @@ EOT
|
|||
$license = $input->getOption('license') ?: false;
|
||||
$license = $dialog->ask(
|
||||
$output,
|
||||
$dialog->getQuestion('License', $license)
|
||||
$dialog->getQuestion('License', $license),
|
||||
$license
|
||||
);
|
||||
$input->setOption('license', $license);
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Installer;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -21,6 +23,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Ryan Weaver <ryan@knplabs.com>
|
||||
* @author Konstantin Kudryashov <ever.zet@gmail.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
|
@ -35,7 +38,8 @@ class InstallCommand extends Command
|
|||
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
|
||||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'),
|
||||
new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
|
||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
||||
|
@ -56,9 +60,18 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$composer = $this->getComposer();
|
||||
if ($input->getOption('no-custom-installers')) {
|
||||
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||
$input->setOption('no-plugins', true);
|
||||
}
|
||||
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
$io = $this->getIO();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$preferSource = false;
|
||||
|
@ -90,10 +103,10 @@ EOT
|
|||
->setOptimizeAutoloader($input->getOption('optimize-autoloader'))
|
||||
;
|
||||
|
||||
if ($input->getOption('no-custom-installers')) {
|
||||
$install->disableCustomInstallers();
|
||||
if ($input->getOption('no-plugins')) {
|
||||
$install->disablePlugins();
|
||||
}
|
||||
|
||||
return $install->run() ? 0 : 1;
|
||||
return $install->run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Helper\TableHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
@ -46,6 +46,10 @@ EOT
|
|||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$composer = $this->getComposer();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'licenses', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$root = $composer->getPackage();
|
||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ use Composer\Factory;
|
|||
use Composer\Installer;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Json\JsonManipulator;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
|
||||
/**
|
||||
* @author Jérémy Romey <jeremy@free-agent.fr>
|
||||
|
@ -80,6 +83,12 @@ EOT
|
|||
$baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array();
|
||||
$requirements = $this->formatRequirements($requirements);
|
||||
|
||||
// validate requirements format
|
||||
$versionParser = new VersionParser();
|
||||
foreach ($requirements as $constraint) {
|
||||
$versionParser->parseConstraints($constraint);
|
||||
}
|
||||
|
||||
if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey)) {
|
||||
foreach ($requirements as $package => $version) {
|
||||
$baseRequirements[$package] = $version;
|
||||
|
@ -99,6 +108,10 @@ EOT
|
|||
$composer = $this->getComposer();
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
$io = $this->getIO();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$install
|
||||
|
@ -110,14 +123,13 @@ EOT
|
|||
->setUpdateWhitelist(array_keys($requirements));
|
||||
;
|
||||
|
||||
if (!$install->run()) {
|
||||
$status = $install->run();
|
||||
if ($status !== 0) {
|
||||
$output->writeln("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
|
||||
file_put_contents($json->getPath(), $composerBackup);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return $status;
|
||||
}
|
||||
|
||||
private function updateFileCleanly($json, array $base, array $new, $requireKey)
|
||||
|
|
|
@ -20,6 +20,8 @@ use Composer\Repository\CompositeRepository;
|
|||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Factory;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
|
||||
/**
|
||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||
|
@ -65,6 +67,11 @@ EOT
|
|||
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
||||
}
|
||||
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'search', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
}
|
||||
|
||||
$onlyName = $input->getOption('only-name');
|
||||
|
||||
$flags = $onlyName ? RepositoryInterface::SEARCH_NAME : RepositoryInterface::SEARCH_FULLTEXT;
|
||||
|
|
|
@ -13,22 +13,36 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Factory;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Downloader\FilesystemException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Igor Wiedler <igor@wiedler.ch>
|
||||
* @author Kevin Ran <kran@adobe.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class SelfUpdateCommand extends Command
|
||||
{
|
||||
const HOMEPAGE = 'getcomposer.org';
|
||||
const OLD_INSTALL_EXT = '-old.phar';
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('self-update')
|
||||
->setAliases(array('selfupdate'))
|
||||
->setDescription('Updates composer.phar to the latest version.')
|
||||
->setDefinition(array(
|
||||
new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'),
|
||||
new InputOption('clean-backups', null, InputOption::VALUE_NONE, 'Delete old backups during an update. This makes the current version of composer the only backup available after the update'),
|
||||
new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The <info>self-update</info> command checks getcomposer.org for newer
|
||||
versions of composer and if found, installs the latest.
|
||||
|
@ -42,52 +56,162 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
||||
$remoteFilesystem = new RemoteFilesystem($this->getIO());
|
||||
$config = Factory::createConfig();
|
||||
$cacheDir = $config->get('cache-dir');
|
||||
$rollbackDir = $config->get('home');
|
||||
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
|
||||
$tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar').'-temp.phar';
|
||||
|
||||
// check if current dir is writable and if not try the cache dir from settings
|
||||
$tmpDir = is_writable(dirname($localFilename)) ? dirname($localFilename) : $cacheDir;
|
||||
|
||||
// check for permissions in local filesystem before start connection process
|
||||
if (!is_writable($tempDirectory = dirname($tempFilename))) {
|
||||
throw new FilesystemException('Composer update failed: the "'.$tempDirectory.'" directory used to download the temp file could not be written');
|
||||
if (!is_writable($tmpDir)) {
|
||||
throw new FilesystemException('Composer update failed: the "'.$tmpDir.'" directory used to download the temp file could not be written');
|
||||
}
|
||||
|
||||
if (!is_writable($localFilename)) {
|
||||
throw new FilesystemException('Composer update failed: the "'.$localFilename. '" file could not be written');
|
||||
throw new FilesystemException('Composer update failed: the "'.$localFilename.'" file could not be written');
|
||||
}
|
||||
|
||||
$protocol = extension_loaded('openssl') ? 'https' : 'http';
|
||||
$rfs = new RemoteFilesystem($this->getIO());
|
||||
$latest = trim($rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
|
||||
if ($input->getOption('rollback')) {
|
||||
return $this->rollback($output, $rollbackDir, $localFilename);
|
||||
}
|
||||
|
||||
if (Composer::VERSION !== $latest) {
|
||||
$output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));
|
||||
$latestVersion = trim($remoteFilesystem->getContents(self::HOMEPAGE, $baseUrl. '/version', false));
|
||||
$updateVersion = $input->getArgument('version') ?: $latestVersion;
|
||||
|
||||
$remoteFilename = $protocol . '://getcomposer.org/composer.phar';
|
||||
if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
|
||||
$output->writeln('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
|
||||
|
||||
$rfs->copy('getcomposer.org', $remoteFilename, $tempFilename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!file_exists($tempFilename)) {
|
||||
$output->writeln('<error>The download of the new composer version failed for an unexpected reason');
|
||||
if (Composer::VERSION === $updateVersion) {
|
||||
$output->writeln('<info>You are already using composer version '.$updateVersion.'.</info>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
chmod($tempFilename, 0777 & ~umask());
|
||||
// test the phar validity
|
||||
$phar = new \Phar($tempFilename);
|
||||
// free the variable to unlock the file
|
||||
unset($phar);
|
||||
rename($tempFilename, $localFilename);
|
||||
} catch (\Exception $e) {
|
||||
@unlink($tempFilename);
|
||||
if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
|
||||
throw $e;
|
||||
$tempFilename = $tmpDir . '/' . basename($localFilename, '.phar').'-temp.phar';
|
||||
$backupFile = sprintf(
|
||||
'%s/%s-%s%s',
|
||||
$rollbackDir,
|
||||
strtr(Composer::RELEASE_DATE, ' :', '_-'),
|
||||
preg_replace('{^([0-9a-f]{7})[0-9a-f]{33}$}', '$1', Composer::VERSION),
|
||||
self::OLD_INSTALL_EXT
|
||||
);
|
||||
|
||||
$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");
|
||||
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename);
|
||||
if (!file_exists($tempFilename)) {
|
||||
$output->writeln('<error>The download of the new composer version failed for an unexpected reason');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// remove saved installations of composer
|
||||
if ($input->getOption('clean-backups')) {
|
||||
$files = $this->getOldInstallationFiles($rollbackDir);
|
||||
|
||||
if (!empty($files)) {
|
||||
$fs = new Filesystem;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$output->writeln('<info>Removing: '.$file);
|
||||
$fs->remove($file);
|
||||
}
|
||||
$output->writeln('<error>The download is corrupted ('.$e->getMessage().').</error>');
|
||||
$output->writeln('<error>Please re-run the self-update command to try again.</error>');
|
||||
}
|
||||
}
|
||||
|
||||
if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
|
||||
$output->writeln('<error>The file is corrupted ('.$err->getMessage().').</error>');
|
||||
$output->writeln('<error>Please re-run the self-update command to try again.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (file_exists($backupFile)) {
|
||||
$output->writeln('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
|
||||
} else {
|
||||
$output->writeln("<info>You are using the latest composer version.</info>");
|
||||
$output->writeln('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
|
||||
}
|
||||
}
|
||||
|
||||
protected function rollback(OutputInterface $output, $rollbackDir, $localFilename)
|
||||
{
|
||||
$rollbackVersion = $this->getLastBackupVersion($rollbackDir);
|
||||
if (!$rollbackVersion) {
|
||||
throw new \UnexpectedValueException('Composer rollback failed: no installation to roll back to in "'.$rollbackDir.'"');
|
||||
}
|
||||
|
||||
if (!is_writable($rollbackDir)) {
|
||||
throw new FilesystemException('Composer rollback failed: the "'.$rollbackDir.'" dir could not be written to');
|
||||
}
|
||||
|
||||
$old = $rollbackDir . '/' . $rollbackVersion . self::OLD_INSTALL_EXT;
|
||||
|
||||
if (!is_file($old)) {
|
||||
throw new FilesystemException('Composer rollback failed: "'.$old.'" could not be found');
|
||||
}
|
||||
if (!is_readable($old)) {
|
||||
throw new FilesystemException('Composer rollback failed: "'.$old.'" could not be read');
|
||||
}
|
||||
|
||||
$oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
|
||||
$output->writeln(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
|
||||
if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
|
||||
$output->writeln('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
|
||||
{
|
||||
try {
|
||||
@chmod($newFilename, 0777 & ~umask());
|
||||
// test the phar validity
|
||||
$phar = new \Phar($newFilename);
|
||||
// free the variable to unlock the file
|
||||
unset($phar);
|
||||
|
||||
// copy current file into installations dir
|
||||
if ($backupTarget && file_exists($localFilename)) {
|
||||
@copy($localFilename, $backupTarget);
|
||||
}
|
||||
|
||||
unset($phar);
|
||||
rename($newFilename, $localFilename);
|
||||
} catch (\Exception $e) {
|
||||
if ($backupTarget) {
|
||||
@unlink($newFilename);
|
||||
}
|
||||
if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLastBackupVersion($rollbackDir)
|
||||
{
|
||||
$files = $this->getOldInstallationFiles($rollbackDir);
|
||||
if (empty($files)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sort($files);
|
||||
|
||||
return basename(end($files), self::OLD_INSTALL_EXT);
|
||||
}
|
||||
|
||||
protected function getOldInstallationFiles($rollbackDir)
|
||||
{
|
||||
return glob($rollbackDir . '/*' . self::OLD_INSTALL_EXT) ?: array();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\DependencyResolver\DefaultPolicy;
|
||||
use Composer\Factory;
|
||||
use Composer\Package\CompletePackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -66,8 +67,9 @@ EOT
|
|||
// init repos
|
||||
$platformRepo = new PlatformRepository;
|
||||
|
||||
$composer = $this->getComposer(false);
|
||||
if ($input->getOption('self')) {
|
||||
$package = $this->getComposer(false)->getPackage();
|
||||
$package = $this->getComposer()->getPackage();
|
||||
$repos = $installedRepo = new ArrayRepository(array($package));
|
||||
} elseif ($input->getOption('platform')) {
|
||||
$repos = $installedRepo = $platformRepo;
|
||||
|
@ -75,15 +77,14 @@ EOT
|
|||
$repos = $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
|
||||
} elseif ($input->getOption('available')) {
|
||||
$installedRepo = $platformRepo;
|
||||
if ($composer = $this->getComposer(false)) {
|
||||
if ($composer) {
|
||||
$repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
|
||||
} else {
|
||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
||||
$repos = new CompositeRepository($defaultRepos);
|
||||
$output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
|
||||
}
|
||||
} elseif ($composer = $this->getComposer(false)) {
|
||||
$composer = $this->getComposer();
|
||||
} elseif ($composer) {
|
||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
||||
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
|
||||
|
@ -94,6 +95,11 @@ EOT
|
|||
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
||||
}
|
||||
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'show', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
}
|
||||
|
||||
// show single package or single version
|
||||
if ($input->getArgument('package') || !empty($package)) {
|
||||
$versions = array();
|
||||
|
@ -178,6 +184,11 @@ EOT
|
|||
}
|
||||
}
|
||||
list($width) = $this->getApplication()->getTerminalDimensions();
|
||||
if (null === $width) {
|
||||
// In case the width is not detected, we're probably running the command
|
||||
// outside of a real terminal, use space without a limit
|
||||
$width = PHP_INT_MAX;
|
||||
}
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$width--;
|
||||
}
|
||||
|
@ -290,7 +301,7 @@ EOT
|
|||
|
||||
if ($type === 'psr-0') {
|
||||
foreach ($autoloads as $name => $path) {
|
||||
$output->writeln(($name ?: '*') . ' => ' . ($path ?: '.'));
|
||||
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
||||
}
|
||||
} elseif ($type === 'classmap') {
|
||||
$output->writeln(implode(', ', $autoloads));
|
||||
|
|
|
@ -16,7 +16,8 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Composer\Downloader\ChangeReportInterface;
|
||||
use Composer\Downloader\VcsDownloader;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Script\ScriptEvents;
|
||||
|
||||
/**
|
||||
|
@ -46,6 +47,10 @@ EOT
|
|||
{
|
||||
// init repos
|
||||
$composer = $this->getComposer();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'status', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$installedRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
|
||||
$dm = $composer->getDownloadManager();
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Installer;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
@ -20,6 +22,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class UpdateCommand extends Command
|
||||
{
|
||||
|
@ -36,11 +39,13 @@ class UpdateCommand extends Command
|
|||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
|
||||
new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'),
|
||||
new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
|
||||
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
|
||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
|
||||
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.')
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The <info>update</info> command reads the composer.json file from the
|
||||
|
@ -60,9 +65,18 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$composer = $this->getComposer();
|
||||
if ($input->getOption('no-custom-installers')) {
|
||||
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||
$input->setOption('no-plugins', true);
|
||||
}
|
||||
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
$io = $this->getIO();
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$preferSource = false;
|
||||
|
@ -94,12 +108,13 @@ EOT
|
|||
->setOptimizeAutoloader($input->getOption('optimize-autoloader'))
|
||||
->setUpdate(true)
|
||||
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
|
||||
->setWhitelistDependencies($input->getOption('with-dependencies'))
|
||||
;
|
||||
|
||||
if ($input->getOption('no-custom-installers')) {
|
||||
$install->disableCustomInstallers();
|
||||
if ($input->getOption('no-plugins')) {
|
||||
$install->disablePlugins();
|
||||
}
|
||||
|
||||
return $install->run() ? 0 : 1;
|
||||
return $install->run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use Symfony\Component\Process\Process;
|
|||
class Compiler
|
||||
{
|
||||
private $version;
|
||||
private $versionDate;
|
||||
|
||||
/**
|
||||
* Compiles composer into a single phar file
|
||||
|
@ -43,6 +44,14 @@ class Compiler
|
|||
}
|
||||
$this->version = trim($process->getOutput());
|
||||
|
||||
$process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__);
|
||||
if ($process->run() != 0) {
|
||||
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.');
|
||||
}
|
||||
$date = new \DateTime(trim($process->getOutput()));
|
||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->versionDate = $date->format('Y-m-d H:i:s');
|
||||
|
||||
$process = new Process('git describe --tags HEAD');
|
||||
if ($process->run() == 0) {
|
||||
$this->version = trim($process->getOutput());
|
||||
|
@ -117,7 +126,7 @@ class Compiler
|
|||
|
||||
private function addFile($phar, $file, $strip = true)
|
||||
{
|
||||
$path = str_replace(dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR, '', $file->getRealPath());
|
||||
$path = strtr(str_replace(dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR, '', $file->getRealPath()), '\\', '/');
|
||||
|
||||
$content = file_get_contents($file);
|
||||
if ($strip) {
|
||||
|
@ -126,7 +135,10 @@ class Compiler
|
|||
$content = "\n".$content."\n";
|
||||
}
|
||||
|
||||
$content = str_replace('@package_version@', $this->version, $content);
|
||||
if ($path === 'src/Composer/Composer.php') {
|
||||
$content = str_replace('@package_version@', $this->version, $content);
|
||||
$content = str_replace('@release_date@', $this->versionDate, $content);
|
||||
}
|
||||
|
||||
$phar->addFromString($path, $content);
|
||||
}
|
||||
|
|
|
@ -16,17 +16,20 @@ use Composer\Package\RootPackageInterface;
|
|||
use Composer\Package\Locker;
|
||||
use Composer\Repository\RepositoryManager;
|
||||
use Composer\Installer\InstallationManager;
|
||||
use Composer\Plugin\PluginManager;
|
||||
use Composer\Downloader\DownloadManager;
|
||||
use Composer\Script\EventDispatcher;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Autoload\AutoloadGenerator;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Konstantin Kudryashiv <ever.zet@gmail.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class Composer
|
||||
{
|
||||
const VERSION = '@package_version@';
|
||||
const RELEASE_DATE = '@release_date@';
|
||||
|
||||
/**
|
||||
* @var Package\RootPackageInterface
|
||||
|
@ -53,13 +56,18 @@ class Composer
|
|||
*/
|
||||
private $installationManager;
|
||||
|
||||
/**
|
||||
* @var Plugin\PluginManager
|
||||
*/
|
||||
private $pluginManager;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var Script\EventDispatcher
|
||||
* @var EventDispatcher\EventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
|
@ -166,7 +174,23 @@ class Composer
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Script\EventDispatcher $eventDispatcher
|
||||
* @param Plugin\PluginManager $manager
|
||||
*/
|
||||
public function setPluginManager(PluginManager $manager)
|
||||
{
|
||||
$this->pluginManager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin\PluginManager
|
||||
*/
|
||||
public function getPluginManager()
|
||||
{
|
||||
return $this->pluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventDispatcher\EventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function setEventDispatcher(EventDispatcher $eventDispatcher)
|
||||
{
|
||||
|
@ -174,7 +198,7 @@ class Composer
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Script\EventDispatcher
|
||||
* @return EventDispatcher\EventDispatcher
|
||||
*/
|
||||
public function getEventDispatcher()
|
||||
{
|
||||
|
|
|
@ -35,6 +35,9 @@ class Config
|
|||
'cache-files-ttl' => null, // fallback to cache-ttl
|
||||
'cache-files-maxsize' => '300MiB',
|
||||
'discard-changes' => false,
|
||||
'autoloader-suffix' => null,
|
||||
'prepend-autoloader' => true,
|
||||
'github-domains' => array('github.com'),
|
||||
);
|
||||
|
||||
public static $defaultRepositories = array(
|
||||
|
|
|
@ -124,7 +124,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
|||
}
|
||||
|
||||
if ($newFile) {
|
||||
chmod($this->file->getPath(), 0600);
|
||||
@chmod($this->file->getPath(), 0600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,14 +165,15 @@ class Application extends BaseApplication
|
|||
|
||||
/**
|
||||
* @param bool $required
|
||||
* @param bool $disablePlugins
|
||||
* @throws JsonValidationException
|
||||
* @return \Composer\Composer
|
||||
*/
|
||||
public function getComposer($required = true)
|
||||
public function getComposer($required = true, $disablePlugins = false)
|
||||
{
|
||||
if (null === $this->composer) {
|
||||
try {
|
||||
$this->composer = Factory::create($this->io);
|
||||
$this->composer = Factory::create($this->io, null, $disablePlugins);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if ($required) {
|
||||
$this->io->write($e->getMessage());
|
||||
|
@ -234,6 +235,14 @@ class Application extends BaseApplication
|
|||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getLongVersion()
|
||||
{
|
||||
return parent::getLongVersion() . ' ' . Composer::RELEASE_DATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -377,6 +377,7 @@ class Pool
|
|||
if ($constraint === null) {
|
||||
return self::MATCH;
|
||||
}
|
||||
|
||||
return $constraint->matches(new VersionConstraint('==', $candidateVersion)) ? self::MATCH : self::MATCH_NAME;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ class Problem
|
|||
// handle php extensions
|
||||
if (0 === stripos($job['packageName'], 'ext-')) {
|
||||
$ext = substr($job['packageName'], 4);
|
||||
$error = extension_loaded($ext) ? 'has the wrong version ('.phpversion($ext).') installed' : 'is missing from your system';
|
||||
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
|
||||
|
||||
return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'.';
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ class Rule
|
|||
// handle php extensions
|
||||
if (0 === strpos($targetName, 'ext-')) {
|
||||
$ext = substr($targetName, 4);
|
||||
$error = extension_loaded($ext) ? 'has the wrong version ('.phpversion($ext).') installed' : 'is missing from your system';
|
||||
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
|
||||
|
||||
$text .= ' -> the requested PHP extension '.$ext.' '.$error.'.';
|
||||
} elseif (0 === strpos($targetName, 'lib-')) {
|
||||
|
|
|
@ -756,7 +756,6 @@ class Solver
|
|||
|
||||
if ($lastLiteral) {
|
||||
unset($this->branches[$lastBranchIndex][self::BRANCH_LITERALS][$lastBranchOffset]);
|
||||
array_values($this->branches[$lastBranchIndex][self::BRANCH_LITERALS]);
|
||||
|
||||
$level = $lastLevel;
|
||||
$this->revert($level);
|
||||
|
|
|
@ -69,7 +69,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
|
||||
// retry downloading if we have an invalid zip file
|
||||
if ($retries && $e instanceof \UnexpectedValueException && $e->getCode() === \ZipArchive::ER_NOZIP) {
|
||||
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
|
||||
$this->io->write(' Invalid zip file, retrying...');
|
||||
usleep(500000);
|
||||
continue;
|
||||
|
@ -132,7 +132,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
*/
|
||||
private function listFiles($dir)
|
||||
{
|
||||
$files = array_merge(glob($dir . '/.*'), glob($dir . '/*'));
|
||||
$files = array_merge(glob($dir . '/.*') ?: array(), glob($dir . '/*') ?: array());
|
||||
|
||||
return array_values(array_filter($files, function ($el) {
|
||||
return basename($el) !== '.' && basename($el) !== '..';
|
||||
|
|
|
@ -24,9 +24,9 @@ interface ChangeReportInterface
|
|||
/**
|
||||
* Checks for changes to the local copy
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path package directory
|
||||
* @return string|null changes or null
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path package directory
|
||||
* @return string|null changes or null
|
||||
*/
|
||||
public function getLocalChanges(PackageInterface $package, $path);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ use Composer\Cache;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PreFileDownloadEvent;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\GitHub;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
|
@ -27,10 +30,10 @@ use Composer\Util\RemoteFilesystem;
|
|||
* @author Kirill chEbba Chebunin <iam@chebba.org>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class FileDownloader implements DownloaderInterface
|
||||
{
|
||||
private static $cacheCollected = false;
|
||||
protected $io;
|
||||
protected $config;
|
||||
protected $rfs;
|
||||
|
@ -41,24 +44,26 @@ class FileDownloader implements DownloaderInterface
|
|||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param IOInterface $io The IO instance
|
||||
* @param Config $config The config
|
||||
* @param Cache $cache Optional cache instance
|
||||
* @param RemoteFilesystem $rfs The remote filesystem
|
||||
* @param Filesystem $filesystem The filesystem
|
||||
* @param IOInterface $io The IO instance
|
||||
* @param Config $config The config
|
||||
* @param EventDispatcher $eventDispatcher The event dispatcher
|
||||
* @param Cache $cache Optional cache instance
|
||||
* @param RemoteFilesystem $rfs The remote filesystem
|
||||
* @param Filesystem $filesystem The filesystem
|
||||
*/
|
||||
public function __construct(IOInterface $io, Config $config, Cache $cache = null, RemoteFilesystem $rfs = null, Filesystem $filesystem = null)
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, RemoteFilesystem $rfs = null, Filesystem $filesystem = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->rfs = $rfs ?: new RemoteFilesystem($io);
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
$this->cache = $cache;
|
||||
|
||||
if ($this->cache && !self::$cacheCollected && !mt_rand(0, 50)) {
|
||||
$this->cache->gc($config->get('cache-ttl'), $config->get('cache-files-maxsize'));
|
||||
|
||||
if ($this->cache && $this->cache->gcIsNecessary()) {
|
||||
$this->cache->gc($config->get('cache-files-ttl'), $config->get('cache-files-maxsize'));
|
||||
}
|
||||
self::$cacheCollected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,6 +84,7 @@ class FileDownloader implements DownloaderInterface
|
|||
throw new \InvalidArgumentException('The given package is missing url information');
|
||||
}
|
||||
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
@ -88,6 +94,12 @@ class FileDownloader implements DownloaderInterface
|
|||
$processedUrl = $this->processUrl($package, $url);
|
||||
$hostname = parse_url($processedUrl, PHP_URL_HOST);
|
||||
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl);
|
||||
if ($this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
}
|
||||
$rfs = $preFileDownloadEvent->getRemoteFilesystem();
|
||||
|
||||
if (strpos($hostname, '.github.com') === (strlen($hostname) - 11)) {
|
||||
$hostname = 'github.com';
|
||||
}
|
||||
|
@ -103,11 +115,11 @@ class FileDownloader implements DownloaderInterface
|
|||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
$this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions());
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions());
|
||||
break;
|
||||
} catch (TransportException $e) {
|
||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||
if ((0 !== $e->getCode() && 500 !== $e->getCode()) || !$retries) {
|
||||
if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
|
||||
throw $e;
|
||||
}
|
||||
if ($this->io->isVerbose()) {
|
||||
|
@ -124,15 +136,18 @@ class FileDownloader implements DownloaderInterface
|
|||
$this->io->write(' Loading from cache');
|
||||
}
|
||||
} catch (TransportException $e) {
|
||||
if (in_array($e->getCode(), array(404, 403)) && 'github.com' === $hostname && !$this->io->hasAuthentication($hostname)) {
|
||||
if (!in_array($e->getCode(), array(404, 403, 412))) {
|
||||
throw $e;
|
||||
}
|
||||
if ('github.com' === $hostname && !$this->io->hasAuthentication($hostname)) {
|
||||
$message = "\n".'Could not fetch '.$processedUrl.', enter your GitHub credentials '.($e->getCode() === 404 ? 'to access private repos' : 'to go over the API rate limit');
|
||||
$gitHubUtil = new GitHub($this->io, $this->config, null, $this->rfs);
|
||||
$gitHubUtil = new GitHub($this->io, $this->config, null, $rfs);
|
||||
if (!$gitHubUtil->authorizeOAuth($hostname)
|
||||
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($hostname, $message))
|
||||
) {
|
||||
throw $e;
|
||||
}
|
||||
$this->rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions());
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getOptions());
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
|
|
|
@ -293,7 +293,7 @@ class GitDownloader extends VcsDownloader
|
|||
}
|
||||
|
||||
// public github, autoswitch protocols
|
||||
if (preg_match('{^(?:https?|git)(://github.com/.*)}', $url, $match)) {
|
||||
if (preg_match('{^(?:https?|git)(://'.$this->getGitHubDomainsRegex().'/.*)}', $url, $match)) {
|
||||
$protocols = $this->config->get('github-protocols');
|
||||
if (!is_array($protocols)) {
|
||||
throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols));
|
||||
|
@ -317,7 +317,7 @@ class GitDownloader extends VcsDownloader
|
|||
$command = call_user_func($commandCallable, $url);
|
||||
if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) {
|
||||
// private github repository without git access, try https with auth
|
||||
if (preg_match('{^git@(github.com):(.+?)\.git$}i', $url, $match)) {
|
||||
if (preg_match('{^git@'.$this->getGitHubDomainsRegex().':(.+?)\.git$}i', $url, $match)) {
|
||||
if (!$this->io->hasAuthentication($match[1])) {
|
||||
$gitHubUtil = new GitHub($this->io, $this->config, $this->process);
|
||||
$message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos';
|
||||
|
@ -368,6 +368,11 @@ class GitDownloader extends VcsDownloader
|
|||
}
|
||||
}
|
||||
|
||||
protected function getGitHubDomainsRegex()
|
||||
{
|
||||
return '('.implode('|', array_map('preg_quote', $this->config->get('github-domains'))).')';
|
||||
}
|
||||
|
||||
protected function throwException($message, $url)
|
||||
{
|
||||
if (0 !== $this->process->execute('git --version', $ignoredOutput)) {
|
||||
|
@ -379,17 +384,17 @@ class GitDownloader extends VcsDownloader
|
|||
|
||||
protected function sanitizeUrl($message)
|
||||
{
|
||||
return preg_replace('{://(.+?):.+?@}', '://$1:***@', $message);
|
||||
return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message);
|
||||
}
|
||||
|
||||
protected function setPushUrl(PackageInterface $package, $path)
|
||||
{
|
||||
// set push url for github projects
|
||||
if (preg_match('{^(?:https?|git)://github.com/([^/]+)/([^/]+?)(?:\.git)?$}', $package->getSourceUrl(), $match)) {
|
||||
if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $package->getSourceUrl(), $match)) {
|
||||
$protocols = $this->config->get('github-protocols');
|
||||
$pushUrl = 'git@github.com:'.$match[1].'/'.$match[2].'.git';
|
||||
$pushUrl = 'git@'.$match[1].':'.$match[2].'/'.$match[3].'.git';
|
||||
if ($protocols[0] !== 'git') {
|
||||
$pushUrl = 'https://github.com/'.$match[1].'/'.$match[2].'.git';
|
||||
$pushUrl = 'https://' . $match[1] . '/'.$match[2].'/'.$match[3].'.git';
|
||||
}
|
||||
$cmd = sprintf('git remote set-url --push origin %s', escapeshellarg($pushUrl));
|
||||
$this->process->execute($cmd, $ignoredOutput, $path);
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Downloader;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\VcsRepository;
|
||||
use Composer\Util\Perforce;
|
||||
|
||||
/**
|
||||
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||
*/
|
||||
class PerforceDownloader extends VcsDownloader
|
||||
{
|
||||
protected $perforce;
|
||||
protected $perforceInjected = false;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doDownload(PackageInterface $package, $path)
|
||||
{
|
||||
$ref = $package->getSourceReference();
|
||||
$label = $package->getPrettyVersion();
|
||||
|
||||
$this->io->write(' Cloning ' . $ref);
|
||||
$this->initPerforce($package, $path);
|
||||
$this->perforce->setStream($ref);
|
||||
$this->perforce->p4Login($this->io);
|
||||
$this->perforce->writeP4ClientSpec();
|
||||
$this->perforce->connectClient();
|
||||
$this->perforce->syncCodeBase($label);
|
||||
$this->perforce->cleanupClientSpec();
|
||||
}
|
||||
|
||||
public function initPerforce($package, $path)
|
||||
{
|
||||
if ($this->perforce) {
|
||||
$this->perforce->initializePath($path);
|
||||
return;
|
||||
}
|
||||
|
||||
$repository = $package->getRepository();
|
||||
$repoConfig = null;
|
||||
if ($repository instanceof VcsRepository) {
|
||||
$repoConfig = $this->getRepoConfig($repository);
|
||||
}
|
||||
$this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path);
|
||||
}
|
||||
|
||||
private function getRepoConfig(VcsRepository $repository)
|
||||
{
|
||||
return $repository->getRepoConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path)
|
||||
{
|
||||
$this->doDownload($target, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getLocalChanges(PackageInterface $package, $path)
|
||||
{
|
||||
$this->io->write('Perforce driver does not check for local changes before overriding', true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||
{
|
||||
$commitLogs = $this->perforce->getCommitLogs($fromReference, $toReference);
|
||||
|
||||
return $commitLogs;
|
||||
}
|
||||
|
||||
public function setPerforce($perforce)
|
||||
{
|
||||
$this->perforce = $perforce;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Downloader;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\Cache;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\IO\IOInterface;
|
||||
use RarArchive;
|
||||
|
||||
/**
|
||||
* RAR archive downloader.
|
||||
*
|
||||
* Based on previous work by Jordi Boggiano ({@see ZipDownloader}).
|
||||
*
|
||||
* @author Derrick Nelson <drrcknlsn@gmail.com>
|
||||
*/
|
||||
class RarDownloader extends ArchiveDownloader
|
||||
{
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $eventDispatcher, $cache);
|
||||
}
|
||||
|
||||
protected function extract($file, $path)
|
||||
{
|
||||
$processError = null;
|
||||
|
||||
// Try to use unrar on *nix
|
||||
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$command = 'unrar x ' . escapeshellarg($file) . ' ' . escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path);
|
||||
|
||||
if (0 === $this->process->execute($command, $ignoredOutput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
|
||||
}
|
||||
|
||||
if (!class_exists('RarArchive')) {
|
||||
// php.ini path is added to the error message to help users find the correct file
|
||||
$iniPath = php_ini_loaded_file();
|
||||
|
||||
if ($iniPath) {
|
||||
$iniMessage = 'The php.ini used by your command-line PHP is: ' . $iniPath;
|
||||
} else {
|
||||
$iniMessage = 'A php.ini file does not exist. You will have to create one.';
|
||||
}
|
||||
|
||||
$error = "Could not decompress the archive, enable the PHP rar extension or install unrar.\n"
|
||||
. $iniMessage . "\n" . $processError;
|
||||
|
||||
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$error = "Could not decompress the archive, enable the PHP rar extension.\n" . $iniMessage;
|
||||
}
|
||||
|
||||
throw new \RuntimeException($error);
|
||||
}
|
||||
|
||||
$rarArchive = RarArchive::open($file);
|
||||
|
||||
if (false === $rarArchive) {
|
||||
throw new \UnexpectedValueException('Could not open RAR archive: ' . $file);
|
||||
}
|
||||
|
||||
$entries = $rarArchive->getEntries();
|
||||
|
||||
if (false === $entries) {
|
||||
throw new \RuntimeException('Could not retrieve RAR archive entries');
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (false === $entry->extract($path)) {
|
||||
throw new \RuntimeException('Could not extract entry');
|
||||
}
|
||||
}
|
||||
|
||||
$rarArchive->close();
|
||||
}
|
||||
}
|
|
@ -148,8 +148,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
* Prompt the user to check if changes should be stashed/removed or the operation aborted
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @param string $path
|
||||
* @param bool $update if true (update) the changes can be stashed and reapplied after an update,
|
||||
* @param string $path
|
||||
* @param bool $update if true (update) the changes can be stashed and reapplied after an update,
|
||||
* if false (remove) the changes should be assumed to be lost if the operation is not aborted
|
||||
* @throws \RuntimeException in case the operation must be aborted
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Downloader;
|
|||
|
||||
use Composer\Config;
|
||||
use Composer\Cache;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\IO\IOInterface;
|
||||
use ZipArchive;
|
||||
|
@ -25,10 +26,10 @@ class ZipDownloader extends ArchiveDownloader
|
|||
{
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, Cache $cache = null, ProcessExecutor $process = null)
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $cache);
|
||||
parent::__construct($io, $config, $eventDispatcher, $cache);
|
||||
}
|
||||
|
||||
protected function extract($file, $path)
|
||||
|
@ -37,7 +38,7 @@ class ZipDownloader extends ArchiveDownloader
|
|||
|
||||
// try to use unzip on *nix
|
||||
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path);
|
||||
$command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path);
|
||||
if (0 === $this->process->execute($command, $ignoredOutput)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\EventDispatcher;
|
||||
|
||||
/**
|
||||
* The base event class
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/**
|
||||
* @var string This event's name
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var boolean Whether the event should not be passed to more listeners
|
||||
*/
|
||||
private $propagationStopped = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event's name.
|
||||
*
|
||||
* @return string The event name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if stopPropagation has been called
|
||||
*
|
||||
* @return boolean Whether propagation has been stopped
|
||||
*/
|
||||
public function isPropagationStopped()
|
||||
{
|
||||
return $this->propagationStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the event from being passed to further listeners
|
||||
*/
|
||||
public function stopPropagation()
|
||||
{
|
||||
$this->propagationStopped = true;
|
||||
}
|
||||
}
|
|
@ -10,11 +10,14 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Script;
|
||||
namespace Composer\EventDispatcher;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\Operation\OperationInterface;
|
||||
use Composer\Script;
|
||||
use Composer\Script\CommandEvent;
|
||||
use Composer\Script\PackageEvent;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +31,7 @@ use Composer\Util\ProcessExecutor;
|
|||
*
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class EventDispatcher
|
||||
{
|
||||
|
@ -51,15 +55,30 @@ class EventDispatcher
|
|||
}
|
||||
|
||||
/**
|
||||
* Dispatch a script event.
|
||||
* Dispatch an event
|
||||
*
|
||||
* @param string $eventName The constant in ScriptEvents
|
||||
* @param string $eventName An event name
|
||||
* @param Event $event
|
||||
*/
|
||||
public function dispatch($eventName, Event $event = null)
|
||||
{
|
||||
if (null == $event) {
|
||||
$event = new Event($eventName, $this->composer, $this->io);
|
||||
$event = new Event($eventName);
|
||||
}
|
||||
|
||||
$this->doDispatch($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a script event.
|
||||
*
|
||||
* @param string $eventName The constant in ScriptEvents
|
||||
* @param Script\Event $event
|
||||
*/
|
||||
public function dispatchScript($eventName, Script\Event $event = null)
|
||||
{
|
||||
if (null == $event) {
|
||||
$event = new Script\Event($eventName, $this->composer, $this->io);
|
||||
}
|
||||
|
||||
$this->doDispatch($event);
|
||||
|
@ -100,7 +119,9 @@ class EventDispatcher
|
|||
$listeners = $this->getListeners($event);
|
||||
|
||||
foreach ($listeners as $callable) {
|
||||
if ($this->isPhpScript($callable)) {
|
||||
if (!is_string($callable) && is_callable($callable)) {
|
||||
call_user_func($callable, $event);
|
||||
} elseif ($this->isPhpScript($callable)) {
|
||||
$className = substr($callable, 0, strpos($callable, '::'));
|
||||
$methodName = substr($callable, strpos($callable, '::') + 2);
|
||||
|
||||
|
@ -127,6 +148,10 @@ class EventDispatcher
|
|||
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
if ($event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,10 +166,67 @@ class EventDispatcher
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a listener for a particular event
|
||||
*
|
||||
* @param string $eventName The event name - typically a constant
|
||||
* @param Callable $listener A callable expecting an event argument
|
||||
* @param integer $priority A higher value represents a higher priority
|
||||
*/
|
||||
protected function addListener($eventName, $listener, $priority = 0)
|
||||
{
|
||||
$this->listeners[$eventName][$priority][] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds object methods as listeners for the events in getSubscribedEvents
|
||||
*
|
||||
* @see EventSubscriberInterface
|
||||
*
|
||||
* @param EventSubscriberInterface $subscriber
|
||||
*/
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
|
||||
if (is_string($params)) {
|
||||
$this->addListener($eventName, array($subscriber, $params));
|
||||
} elseif (is_string($params[0])) {
|
||||
$this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
|
||||
} else {
|
||||
foreach ($params as $listener) {
|
||||
$this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all listeners for a given event
|
||||
*
|
||||
* @param Event $event
|
||||
* @return array All listeners: callables and scripts
|
||||
*/
|
||||
protected function getListeners(Event $event)
|
||||
{
|
||||
$scriptListeners = $this->getScriptListeners($event);
|
||||
|
||||
if (!isset($this->listeners[$event->getName()][0])) {
|
||||
$this->listeners[$event->getName()][0] = array();
|
||||
}
|
||||
krsort($this->listeners[$event->getName()]);
|
||||
|
||||
$listeners = $this->listeners;
|
||||
$listeners[$event->getName()][0] = array_merge($listeners[$event->getName()][0], $scriptListeners);
|
||||
|
||||
return call_user_func_array('array_merge', $listeners[$event->getName()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all listeners defined as scripts in the package
|
||||
*
|
||||
* @param Event $event Event object
|
||||
* @return array Listeners
|
||||
*/
|
||||
protected function getListeners(Event $event)
|
||||
protected function getScriptListeners(Event $event)
|
||||
{
|
||||
$package = $this->composer->getPackage();
|
||||
$scripts = $package->getScripts();
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\EventDispatcher;
|
||||
|
||||
/**
|
||||
* An EventSubscriber knows which events it is interested in.
|
||||
*
|
||||
* If an EventSubscriber is added to an EventDispatcher, the manager invokes
|
||||
* {@link getSubscribedEvents} and registers the subscriber as a listener for all
|
||||
* returned events.
|
||||
*
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array of event names this subscriber wants to listen to.
|
||||
*
|
||||
* The array keys are event names and the value can be:
|
||||
*
|
||||
* * The method name to call (priority defaults to 0)
|
||||
* * An array composed of the method name to call and the priority
|
||||
* * An array of arrays composed of the method names to call and respective
|
||||
* priorities, or 0 if unset
|
||||
*
|
||||
* For instance:
|
||||
*
|
||||
* * array('eventName' => 'methodName')
|
||||
* * array('eventName' => array('methodName', $priority))
|
||||
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
|
||||
*
|
||||
* @return array The event names to listen to
|
||||
*/
|
||||
public static function getSubscribedEvents();
|
||||
}
|
|
@ -16,12 +16,12 @@ use Composer\Config\JsonConfigSource;
|
|||
use Composer\Json\JsonFile;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\Archiver;
|
||||
use Composer\Repository\ComposerRepository;
|
||||
use Composer\Repository\RepositoryManager;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Composer\Script\EventDispatcher;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Autoload\AutoloadGenerator;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
|
||||
|
@ -31,6 +31,7 @@ use Composer\Package\Version\VersionParser;
|
|||
* @author Ryan Weaver <ryan@knplabs.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Igor Wiedler <igor@wiedler.ch>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
|
@ -117,7 +118,9 @@ class Factory
|
|||
@rename($child, $dir.'/'.basename($child));
|
||||
}
|
||||
}
|
||||
@rmdir($oldPath);
|
||||
if ($config->get('cache-dir') != $oldPath) {
|
||||
@rmdir($oldPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,11 +179,12 @@ class Factory
|
|||
* @param IOInterface $io IO instance
|
||||
* @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will
|
||||
* read from the default filename
|
||||
* @param bool $disablePlugins Whether plugins should not be loaded
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \UnexpectedValueException
|
||||
* @return Composer
|
||||
*/
|
||||
public function createComposer(IOInterface $io, $localConfig = null)
|
||||
public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false)
|
||||
{
|
||||
// load Composer configuration
|
||||
if (null === $localConfig) {
|
||||
|
@ -216,8 +220,15 @@ class Factory
|
|||
// setup process timeout
|
||||
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
|
||||
|
||||
// initialize composer
|
||||
$composer = new Composer();
|
||||
$composer->setConfig($config);
|
||||
|
||||
// initialize event dispatcher
|
||||
$dispatcher = new EventDispatcher($composer, $io);
|
||||
|
||||
// initialize repository manager
|
||||
$rm = $this->createRepositoryManager($io, $config);
|
||||
$rm = $this->createRepositoryManager($io, $config, $dispatcher);
|
||||
|
||||
// load local repository
|
||||
$this->addLocalRepository($rm, $vendorDir);
|
||||
|
@ -227,22 +238,18 @@ class Factory
|
|||
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io));
|
||||
$package = $loader->load($localConfig);
|
||||
|
||||
// initialize download manager
|
||||
$dm = $this->createDownloadManager($io, $config);
|
||||
|
||||
// initialize installation manager
|
||||
$im = $this->createInstallationManager();
|
||||
|
||||
// initialize composer
|
||||
$composer = new Composer();
|
||||
$composer->setConfig($config);
|
||||
// Composer composition
|
||||
$composer->setPackage($package);
|
||||
$composer->setRepositoryManager($rm);
|
||||
$composer->setDownloadManager($dm);
|
||||
$composer->setInstallationManager($im);
|
||||
|
||||
// initialize event dispatcher
|
||||
$dispatcher = new EventDispatcher($composer, $io);
|
||||
// initialize download manager
|
||||
$dm = $this->createDownloadManager($io, $config, $dispatcher);
|
||||
|
||||
$composer->setDownloadManager($dm);
|
||||
$composer->setEventDispatcher($dispatcher);
|
||||
|
||||
// initialize autoload generator
|
||||
|
@ -252,6 +259,14 @@ class Factory
|
|||
// add installers to the manager
|
||||
$this->createDefaultInstallers($im, $composer, $io);
|
||||
|
||||
$globalRepository = $this->createGlobalRepository($config, $vendorDir);
|
||||
$pm = $this->createPluginManager($composer, $io, $globalRepository);
|
||||
$composer->setPluginManager($pm);
|
||||
|
||||
if (!$disablePlugins) {
|
||||
$pm->loadInstalledPlugins();
|
||||
}
|
||||
|
||||
// purge packages if they have been deleted on the filesystem
|
||||
$this->purgePackages($rm, $im);
|
||||
|
||||
|
@ -270,17 +285,19 @@ class Factory
|
|||
/**
|
||||
* @param IOInterface $io
|
||||
* @param Config $config
|
||||
* @param EventDispatcher $eventDispatcher
|
||||
* @return Repository\RepositoryManager
|
||||
*/
|
||||
protected function createRepositoryManager(IOInterface $io, Config $config)
|
||||
protected function createRepositoryManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
$rm = new RepositoryManager($io, $config);
|
||||
$rm = new RepositoryManager($io, $config, $eventDispatcher);
|
||||
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
|
||||
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
|
||||
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
|
||||
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
|
||||
$rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository');
|
||||
$rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository');
|
||||
$rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository');
|
||||
$rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository');
|
||||
$rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository');
|
||||
|
||||
|
@ -296,12 +313,31 @@ class Factory
|
|||
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $config
|
||||
* @param string $vendorDir
|
||||
*/
|
||||
protected function createGlobalRepository(Config $config, $vendorDir)
|
||||
{
|
||||
if ($config->get('home') == $vendorDir) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = $config->get('home').'/vendor/composer/installed.json';
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Repository\InstalledFilesystemRepository(new JsonFile($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IO\IOInterface $io
|
||||
* @param Config $config
|
||||
* @param EventDispatcher $eventDispatcher
|
||||
* @return Downloader\DownloadManager
|
||||
*/
|
||||
public function createDownloadManager(IOInterface $io, Config $config)
|
||||
public function createDownloadManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
$cache = null;
|
||||
if ($config->get('cache-files-ttl') > 0) {
|
||||
|
@ -325,10 +361,12 @@ class Factory
|
|||
$dm->setDownloader('git', new Downloader\GitDownloader($io, $config));
|
||||
$dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config));
|
||||
$dm->setDownloader('hg', new Downloader\HgDownloader($io, $config));
|
||||
$dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $cache));
|
||||
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $cache));
|
||||
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $cache));
|
||||
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $cache));
|
||||
$dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config));
|
||||
$dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $eventDispatcher, $cache));
|
||||
$dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache));
|
||||
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache));
|
||||
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache));
|
||||
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache));
|
||||
|
||||
return $dm;
|
||||
}
|
||||
|
@ -342,7 +380,9 @@ class Factory
|
|||
public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null)
|
||||
{
|
||||
if (null === $dm) {
|
||||
$dm = $this->createDownloadManager(new IO\NullIO(), $config);
|
||||
$io = new IO\NullIO();
|
||||
$io->loadConfiguration($config);
|
||||
$dm = $this->createDownloadManager($io, $config);
|
||||
}
|
||||
|
||||
$am = new Archiver\ArchiveManager($dm);
|
||||
|
@ -351,6 +391,14 @@ class Factory
|
|||
return $am;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin\PluginManager
|
||||
*/
|
||||
protected function createPluginManager(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null)
|
||||
{
|
||||
return new Plugin\PluginManager($composer, $io, $globalRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Installer\InstallationManager
|
||||
*/
|
||||
|
@ -368,7 +416,7 @@ class Factory
|
|||
{
|
||||
$im->addInstaller(new Installer\LibraryInstaller($io, $composer, null));
|
||||
$im->addInstaller(new Installer\PearInstaller($io, $composer, 'pear-library'));
|
||||
$im->addInstaller(new Installer\InstallerInstaller($io, $composer));
|
||||
$im->addInstaller(new Installer\PluginInstaller($io, $composer));
|
||||
$im->addInstaller(new Installer\MetapackageInstaller($io));
|
||||
}
|
||||
|
||||
|
@ -390,12 +438,13 @@ class Factory
|
|||
* @param IOInterface $io IO instance
|
||||
* @param mixed $config either a configuration array or a filename to read from, if null it will read from
|
||||
* the default filename
|
||||
* @param bool $disablePlugins Whether plugins should not be loaded
|
||||
* @return Composer
|
||||
*/
|
||||
public static function create(IOInterface $io, $config = null)
|
||||
public static function create(IOInterface $io, $config = null, $disablePlugins = false)
|
||||
{
|
||||
$factory = new static();
|
||||
|
||||
return $factory->createComposer($io, $config);
|
||||
return $factory->createComposer($io, $config, $disablePlugins);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
namespace Composer\IO;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Composer\Config;
|
||||
|
||||
abstract class BaseIO implements IOInterface
|
||||
|
|
|
@ -71,7 +71,7 @@ interface IOInterface
|
|||
* @param bool $newline Whether to add a newline or not
|
||||
* @param integer $size The size of line
|
||||
*/
|
||||
public function overwrite($messages, $newline = true, $size = 80);
|
||||
public function overwrite($messages, $newline = true, $size = null);
|
||||
|
||||
/**
|
||||
* Asks a question to the user.
|
||||
|
|
|
@ -24,6 +24,7 @@ use Composer\DependencyResolver\Rule;
|
|||
use Composer\DependencyResolver\Solver;
|
||||
use Composer\DependencyResolver\SolverProblemsException;
|
||||
use Composer\Downloader\DownloadManager;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Installer\InstallationManager;
|
||||
use Composer\Config;
|
||||
use Composer\Installer\NoopInstaller;
|
||||
|
@ -41,13 +42,13 @@ use Composer\Repository\InstalledFilesystemRepository;
|
|||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Repository\RepositoryManager;
|
||||
use Composer\Script\EventDispatcher;
|
||||
use Composer\Script\ScriptEvents;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Beau Simensen <beau@dflydev.com>
|
||||
* @author Konstantin Kudryashov <ever.zet@gmail.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class Installer
|
||||
{
|
||||
|
@ -105,6 +106,7 @@ class Installer
|
|||
protected $update = false;
|
||||
protected $runScripts = true;
|
||||
protected $updateWhitelist = null;
|
||||
protected $whitelistDependencies = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
|
@ -144,6 +146,8 @@ class Installer
|
|||
|
||||
/**
|
||||
* Run installation (or update)
|
||||
*
|
||||
* @return int 0 on success or a positive error code on failure
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
@ -169,13 +173,15 @@ class Installer
|
|||
unset($devRepo, $package);
|
||||
// end BC
|
||||
|
||||
if ($this->preferSource) {
|
||||
$this->downloadManager->setPreferSource(true);
|
||||
}
|
||||
if ($this->preferDist) {
|
||||
$this->downloadManager->setPreferDist(true);
|
||||
if ($this->runScripts) {
|
||||
// dispatch pre event
|
||||
$eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
|
||||
$this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
|
||||
}
|
||||
|
||||
$this->downloadManager->setPreferSource($this->preferSource);
|
||||
$this->downloadManager->setPreferDist($this->preferDist);
|
||||
|
||||
// clone root package to have one in the installed repo that does not require anything
|
||||
// we don't want it to be uninstallable, but its requirements should not conflict
|
||||
// with the lock file for example
|
||||
|
@ -199,16 +205,11 @@ class Installer
|
|||
$aliases = $this->getRootAliases();
|
||||
$this->aliasPlatformPackages($platformRepo, $aliases);
|
||||
|
||||
if ($this->runScripts) {
|
||||
// dispatch pre event
|
||||
$eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
|
||||
$this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->suggestedPackages = array();
|
||||
if (!$this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $this->devMode)) {
|
||||
return false;
|
||||
$res = $this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $this->devMode);
|
||||
if ($res !== 0) {
|
||||
return $res;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->installationManager->notifyInstalls();
|
||||
|
@ -288,7 +289,7 @@ class Installer
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs)
|
||||
|
@ -450,7 +451,7 @@ class Installer
|
|||
$this->io->write('<error>Your requirements could not be resolved to an installable set of packages.</error>');
|
||||
$this->io->write($e->getMessage());
|
||||
|
||||
return false;
|
||||
return max(1, $e->getCode());
|
||||
}
|
||||
|
||||
// force dev packages to be updated if we update or install from a (potentially new) lock
|
||||
|
@ -461,7 +462,7 @@ class Installer
|
|||
$this->io->write('Nothing to install or update');
|
||||
}
|
||||
|
||||
$operations = $this->moveCustomInstallersToFront($operations);
|
||||
$operations = $this->movePluginsToFront($operations);
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
// collect suggestions
|
||||
|
@ -535,12 +536,11 @@ class Installer
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Workaround: if your packages depend on custom installers, we must be sure
|
||||
* Workaround: if your packages depend on plugins, we must be sure
|
||||
* that those are installed / updated first; else it would lead to packages
|
||||
* being installed multiple times in different folders, when running Composer
|
||||
* twice.
|
||||
|
@ -549,22 +549,22 @@ class Installer
|
|||
* it at least fixes the symptoms and makes usage of composer possible (again)
|
||||
* in such scenarios.
|
||||
*
|
||||
* @param OperationInterface[] $operations
|
||||
* @param OperationInterface[] $operations
|
||||
* @return OperationInterface[] reordered operation list
|
||||
*/
|
||||
private function moveCustomInstallersToFront(array $operations)
|
||||
private function movePluginsToFront(array $operations)
|
||||
{
|
||||
$installerOps = array();
|
||||
foreach ($operations as $idx => $op) {
|
||||
if ($op instanceof InstallOperation) {
|
||||
$package = $op->getPackage();
|
||||
} else if ($op instanceof UpdateOperation) {
|
||||
} elseif ($op instanceof UpdateOperation) {
|
||||
$package = $op->getTargetPackage();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($package->getRequires() === array() && $package->getType() === 'composer-installer') {
|
||||
if ($package->getRequires() === array() && ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer')) {
|
||||
$installerOps[] = $op;
|
||||
unset($operations[$idx]);
|
||||
}
|
||||
|
@ -835,7 +835,7 @@ class Installer
|
|||
|
||||
$depPackages = $pool->whatProvides($packageName);
|
||||
if (count($depPackages) == 0 && !in_array($packageName, $requiredPackageNames) && !in_array($packageName, array('nothing', 'lock'))) {
|
||||
$this->io->write('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.<warning>');
|
||||
$this->io->write('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
|
||||
}
|
||||
|
||||
foreach ($depPackages as $depPackage) {
|
||||
|
@ -851,11 +851,12 @@ class Installer
|
|||
$seen[$package->getId()] = true;
|
||||
$this->updateWhitelist[$package->getName()] = true;
|
||||
|
||||
$requires = $package->getRequires();
|
||||
if ($devMode) {
|
||||
$requires = array_merge($requires, $package->getDevRequires());
|
||||
if (!$this->whitelistDependencies) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$requires = $package->getRequires();
|
||||
|
||||
foreach ($requires as $require) {
|
||||
$requirePackages = $pool->whatProvides($require->getTarget());
|
||||
|
||||
|
@ -1055,7 +1056,20 @@ class Installer
|
|||
}
|
||||
|
||||
/**
|
||||
* Disables custom installers.
|
||||
* Should dependencies of whitelisted packages be updated recursively?
|
||||
*
|
||||
* @param boolean $updateDependencies
|
||||
* @return Installer
|
||||
*/
|
||||
public function setWhitelistDependencies($updateDependencies = true)
|
||||
{
|
||||
$this->whitelistDependencies = (boolean) $updateDependencies;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables plugins.
|
||||
*
|
||||
* Call this if you want to ensure that third-party code never gets
|
||||
* executed. The default is to automatically install, and execute
|
||||
|
@ -1063,9 +1077,9 @@ class Installer
|
|||
*
|
||||
* @return Installer
|
||||
*/
|
||||
public function disableCustomInstallers()
|
||||
public function disablePlugins()
|
||||
{
|
||||
$this->installationManager->disableCustomInstallers();
|
||||
$this->installationManager->disablePlugins();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Installer;
|
|||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Plugin\PluginInstaller;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\DependencyResolver\Operation\OperationInterface;
|
||||
|
@ -29,6 +30,7 @@ use Composer\Util\StreamContextFactory;
|
|||
*
|
||||
* @author Konstantin Kudryashov <ever.zet@gmail.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class InstallationManager
|
||||
{
|
||||
|
@ -66,16 +68,16 @@ class InstallationManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Disables custom installers.
|
||||
* Disables plugins.
|
||||
*
|
||||
* We prevent any custom installers from being instantiated by simply
|
||||
* We prevent any plugins from being instantiated by simply
|
||||
* deactivating the installer for them. This ensure that no third-party
|
||||
* code is ever executed.
|
||||
*/
|
||||
public function disableCustomInstallers()
|
||||
public function disablePlugins()
|
||||
{
|
||||
foreach ($this->installers as $i => $installer) {
|
||||
if (!$installer instanceof InstallerInstaller) {
|
||||
if (!$installer instanceof PluginInstaller) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Installer;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Package\Package;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* Installer installation manager.
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class InstallerInstaller extends LibraryInstaller
|
||||
{
|
||||
private $installationManager;
|
||||
private static $classCounter = 0;
|
||||
|
||||
/**
|
||||
* Initializes Installer installer.
|
||||
*
|
||||
* @param IOInterface $io
|
||||
* @param Composer $composer
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct(IOInterface $io, Composer $composer, $type = 'library')
|
||||
{
|
||||
parent::__construct($io, $composer, 'composer-installer');
|
||||
$this->installationManager = $composer->getInstallationManager();
|
||||
|
||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
foreach ($repo->getPackages() as $package) {
|
||||
if ('composer-installer' === $package->getType()) {
|
||||
$this->registerInstaller($package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
$extra = $package->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
|
||||
parent::install($repo, $package);
|
||||
$this->registerInstaller($package);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
|
||||
{
|
||||
$extra = $target->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
|
||||
parent::update($repo, $initial, $target);
|
||||
$this->registerInstaller($target);
|
||||
}
|
||||
|
||||
private function registerInstaller(PackageInterface $package)
|
||||
{
|
||||
$downloadPath = $this->getInstallPath($package);
|
||||
|
||||
$extra = $package->getExtra();
|
||||
$classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
|
||||
|
||||
$generator = $this->composer->getAutoloadGenerator();
|
||||
$map = $generator->parseAutoloads(array(array($package, $downloadPath)), new Package('dummy', '1.0.0.0', '1.0.0'));
|
||||
$classLoader = $generator->createLoader($map);
|
||||
$classLoader->register();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if (class_exists($class, false)) {
|
||||
$code = file_get_contents($classLoader->findFile($class));
|
||||
$code = preg_replace('{^(\s*)class\s+(\S+)}mi', '$1class $2_composer_tmp'.self::$classCounter, $code);
|
||||
eval('?>'.$code);
|
||||
$class .= '_composer_tmp'.self::$classCounter;
|
||||
self::$classCounter++;
|
||||
}
|
||||
|
||||
$installer = new $class($this->io, $this->composer);
|
||||
$this->installationManager->addInstaller($installer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ namespace Composer\Installer;
|
|||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Downloader\DownloadManager;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
|
@ -41,15 +40,16 @@ class LibraryInstaller implements InstallerInterface
|
|||
* @param IOInterface $io
|
||||
* @param Composer $composer
|
||||
* @param string $type
|
||||
* @param Filesystem $filesystem
|
||||
*/
|
||||
public function __construct(IOInterface $io, Composer $composer, $type = 'library')
|
||||
public function __construct(IOInterface $io, Composer $composer, $type = 'library', Filesystem $filesystem = null)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->downloadManager = $composer->getDownloadManager();
|
||||
$this->io = $io;
|
||||
$this->type = $type;
|
||||
|
||||
$this->filesystem = new Filesystem();
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
$this->vendorDir = rtrim($composer->getConfig()->get('vendor-dir'), '/');
|
||||
$this->binDir = rtrim($composer->getConfig()->get('bin-dir'), '/');
|
||||
}
|
||||
|
@ -157,8 +157,23 @@ class LibraryInstaller implements InstallerInterface
|
|||
|
||||
protected function updateCode(PackageInterface $initial, PackageInterface $target)
|
||||
{
|
||||
$downloadPath = $this->getInstallPath($initial);
|
||||
$this->downloadManager->update($initial, $target, $downloadPath);
|
||||
$initialDownloadPath = $this->getInstallPath($initial);
|
||||
$targetDownloadPath = $this->getInstallPath($target);
|
||||
if ($targetDownloadPath !== $initialDownloadPath) {
|
||||
// if the target and initial dirs intersect, we force a remove + install
|
||||
// to avoid the rename wiping the target dir as part of the initial dir cleanup
|
||||
if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath
|
||||
|| substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath
|
||||
) {
|
||||
$this->removeCode($initial);
|
||||
$this->installCode($target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->filesystem->rename($initialDownloadPath, $targetDownloadPath);
|
||||
}
|
||||
$this->downloadManager->update($initial, $target, $targetDownloadPath);
|
||||
}
|
||||
|
||||
protected function removeCode(PackageInterface $package)
|
||||
|
@ -185,6 +200,12 @@ class LibraryInstaller implements InstallerInterface
|
|||
continue;
|
||||
}
|
||||
|
||||
// in case a custom installer returned a relative path for the
|
||||
// $package, we can now safely turn it into a absolute path (as we
|
||||
// already checked the binary's existence). The following helpers
|
||||
// will require absolute paths to work properly.
|
||||
$binPath = realpath($binPath);
|
||||
|
||||
$this->initializeBinDir();
|
||||
$link = $this->binDir.'/'.basename($bin);
|
||||
if (file_exists($link)) {
|
||||
|
@ -192,7 +213,7 @@ class LibraryInstaller implements InstallerInterface
|
|||
// likely leftover from a previous install, make sure
|
||||
// that the target is still executable in case this
|
||||
// is a fresh install of the vendor.
|
||||
chmod($link, 0777 & ~umask());
|
||||
@chmod($link, 0777 & ~umask());
|
||||
}
|
||||
$this->io->write(' Skipped installation of '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
|
||||
continue;
|
||||
|
@ -201,7 +222,7 @@ class LibraryInstaller implements InstallerInterface
|
|||
// add unixy support for cygwin and similar environments
|
||||
if ('.bat' !== substr($binPath, -4)) {
|
||||
file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link));
|
||||
chmod($link, 0777 & ~umask());
|
||||
@chmod($link, 0777 & ~umask());
|
||||
$link .= '.bat';
|
||||
if (file_exists($link)) {
|
||||
$this->io->write(' Skipped installation of '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
|
||||
|
@ -225,7 +246,7 @@ class LibraryInstaller implements InstallerInterface
|
|||
}
|
||||
chdir($cwd);
|
||||
}
|
||||
chmod($link, 0777 & ~umask());
|
||||
@chmod($link, 0777 & ~umask());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,9 +99,9 @@ class PearInstaller extends LibraryInstaller
|
|||
{
|
||||
parent::initializeBinDir();
|
||||
file_put_contents($this->binDir.'/composer-php', $this->generateUnixyPhpProxyCode());
|
||||
chmod($this->binDir.'/composer-php', 0777);
|
||||
@chmod($this->binDir.'/composer-php', 0777);
|
||||
file_put_contents($this->binDir.'/composer-php.bat', $this->generateWindowsPhpProxyCode());
|
||||
chmod($this->binDir.'/composer-php.bat', 0777);
|
||||
@chmod($this->binDir.'/composer-php.bat', 0777);
|
||||
}
|
||||
|
||||
protected function generateWindowsProxyCode($bin, $link)
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Installer;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Package\Package;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* Installer for plugin packages
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class PluginInstaller extends LibraryInstaller
|
||||
{
|
||||
private $installationManager;
|
||||
private static $classCounter = 0;
|
||||
|
||||
/**
|
||||
* Initializes Plugin installer.
|
||||
*
|
||||
* @param IOInterface $io
|
||||
* @param Composer $composer
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct(IOInterface $io, Composer $composer, $type = 'library')
|
||||
{
|
||||
parent::__construct($io, $composer, 'composer-plugin');
|
||||
$this->installationManager = $composer->getInstallationManager();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supports($packageType)
|
||||
{
|
||||
return $packageType === 'composer-plugin' || $packageType === 'composer-installer';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
$extra = $package->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
|
||||
parent::install($repo, $package);
|
||||
$this->composer->getPluginManager()->registerPackage($package);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
|
||||
{
|
||||
$extra = $target->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
|
||||
parent::update($repo, $initial, $target);
|
||||
$this->composer->getPluginManager()->registerPackage($target);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ namespace Composer\Installer;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Downloader\DownloadManager;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* Project Installer is used to install a single package into a directory as
|
||||
|
@ -26,11 +27,13 @@ class ProjectInstaller implements InstallerInterface
|
|||
{
|
||||
private $installPath;
|
||||
private $downloadManager;
|
||||
private $filesystem;
|
||||
|
||||
public function __construct($installPath, DownloadManager $dm)
|
||||
{
|
||||
$this->installPath = rtrim(strtr($installPath, '\\', '/'), '/').'/';
|
||||
$this->downloadManager = $dm;
|
||||
$this->filesystem = new Filesystem;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +61,7 @@ class ProjectInstaller implements InstallerInterface
|
|||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
$installPath = $this->installPath;
|
||||
if (file_exists($installPath) && (count(glob($installPath.'*')) || (count(glob($installPath.'.*')) > 2))) {
|
||||
if (file_exists($installPath) && !$this->filesystem->isDirEmpty($installPath)) {
|
||||
throw new \InvalidArgumentException("Project directory $installPath is not empty.");
|
||||
}
|
||||
if (!is_dir($installPath)) {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
namespace Composer\Json;
|
||||
|
||||
use Composer\Composer;
|
||||
use JsonSchema\Validator;
|
||||
use Seld\JsonLint\JsonParser;
|
||||
use Seld\JsonLint\ParsingException;
|
||||
|
@ -117,7 +116,21 @@ class JsonFile
|
|||
);
|
||||
}
|
||||
}
|
||||
file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : ''));
|
||||
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : ''));
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if ($retries) {
|
||||
usleep(500000);
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ use Composer\Downloader\DownloadManager;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\RootPackage;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Json\JsonFile;
|
||||
|
||||
/**
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
|
@ -83,9 +84,11 @@ class ArchiveManager
|
|||
$nameParts[] = substr(sha1($package->getSourceReference()), 0, 6);
|
||||
}
|
||||
|
||||
return implode('-', array_filter($nameParts, function ($p) {
|
||||
$name = implode('-', array_filter($nameParts, function ($p) {
|
||||
return !empty($p);
|
||||
}));
|
||||
|
||||
return str_replace('/', '-', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,12 +142,21 @@ class ArchiveManager
|
|||
|
||||
// Download sources
|
||||
$this->downloadManager->download($package, $sourcePath);
|
||||
|
||||
// Check exclude from downloaded composer.json
|
||||
if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) {
|
||||
$jsonFile = new JsonFile($composerJsonPath);
|
||||
$jsonData = $jsonFile->read();
|
||||
if (!empty($jsonData['archive']['exclude'])) {
|
||||
$package->setArchiveExcludes($jsonData['archive']['exclude']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the archive
|
||||
$archivePath = $usableArchiver->archive($sourcePath, $target, $format, $package->getArchiveExcludes());
|
||||
|
||||
//cleanup temporary download
|
||||
// cleanup temporary download
|
||||
if (!$package instanceof RootPackage) {
|
||||
$filesystem->removeDirectory($sourcePath);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ use Composer\Package\BasePackage;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\CompletePackageInterface;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Package\Link;
|
||||
|
||||
/**
|
||||
* @author Konstantin Kudryashiv <ever.zet@gmail.com>
|
||||
|
|
|
@ -66,6 +66,22 @@ class VersionConstraint extends SpecificConstraint
|
|||
* @return bool
|
||||
*/
|
||||
public function matchSpecific(VersionConstraint $provider, $compareBranches = false)
|
||||
{
|
||||
static $cache = array();
|
||||
if (isset($cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches])) {
|
||||
return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches];
|
||||
}
|
||||
|
||||
return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches] =
|
||||
$this->doMatchSpecific($provider, $compareBranches);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VersionConstraint $provider
|
||||
* @param bool $compareBranches
|
||||
* @return bool
|
||||
*/
|
||||
private function doMatchSpecific(VersionConstraint $provider, $compareBranches = false)
|
||||
{
|
||||
$noEqualOp = str_replace('=', '', $this->operator);
|
||||
$providerNoEqualOp = str_replace('=', '', $provider->operator);
|
||||
|
|
|
@ -275,7 +275,14 @@ class VersionParser
|
|||
// version, to ensure that unstable instances of the current version are allowed.
|
||||
// however, if a stability suffix is added to the constraint, then a >= match on the current version is
|
||||
// used instead
|
||||
if (preg_match('{^~(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex.'?$}i', $constraint, $matches)) {
|
||||
if (preg_match('{^~>?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex.'?$}i', $constraint, $matches)) {
|
||||
if (substr($constraint, 0, 2) === '~>') {
|
||||
throw new \UnexpectedValueException(
|
||||
'Could not parse version constraint '.$constraint.': '.
|
||||
'Invalid operator "~>", you probably meant to use the "~" operator'
|
||||
);
|
||||
}
|
||||
|
||||
// Work out which position in the version we are operating at
|
||||
if (isset($matches[4]) && '' !== $matches[4]) {
|
||||
$position = 4;
|
||||
|
@ -368,9 +375,10 @@ class VersionParser
|
|||
*
|
||||
* Support function for {@link parseConstraint()}
|
||||
*
|
||||
* @param array $matches Array with version parts in array indexes 1,2,3,4
|
||||
* @param int $position 1,2,3,4 - which segment of the version to decrement
|
||||
* @param string $pad The string to pad version parts after $position
|
||||
* @param array $matches Array with version parts in array indexes 1,2,3,4
|
||||
* @param int $position 1,2,3,4 - which segment of the version to decrement
|
||||
* @param int $increment
|
||||
* @param string $pad The string to pad version parts after $position
|
||||
* @return string The new version
|
||||
*/
|
||||
private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
|
||||
|
@ -378,7 +386,7 @@ class VersionParser
|
|||
for ($i = 4; $i > 0; $i--) {
|
||||
if ($i > $position) {
|
||||
$matches[$i] = $pad;
|
||||
} else if ($i == $position && $increment) {
|
||||
} elseif ($i == $position && $increment) {
|
||||
$matches[$i] += $increment;
|
||||
// If $matches[$i] was 0, carry the decrement
|
||||
if ($matches[$i] < 0) {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Plugin;
|
||||
|
||||
use Composer\EventDispatcher\Event;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* An event for all commands.
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class CommandEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $commandName;
|
||||
|
||||
/**
|
||||
* @var InputInterface
|
||||
*/
|
||||
private $input;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $commandName The command name
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*/
|
||||
public function __construct($name, $commandName, $input, $output)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->commandName = $commandName;
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command input interface
|
||||
*
|
||||
* @return InputInterface
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the command output interface
|
||||
*
|
||||
* @return OutputInterface
|
||||
*/
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the command being run
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCommandName()
|
||||
{
|
||||
return $this->commandName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Plugin;
|
||||
|
||||
/**
|
||||
* The Plugin Events.
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class PluginEvents
|
||||
{
|
||||
/**
|
||||
* The COMMAND event occurs as a command begins
|
||||
*
|
||||
* The event listener method receives a
|
||||
* Composer\Plugin\CommandEvent instance.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COMMAND = 'command';
|
||||
|
||||
/**
|
||||
* The PRE_FILE_DOWNLOAD event occurs before downloading a file
|
||||
*
|
||||
* The event listener method receives a
|
||||
* Composer\Plugin\PreFileDownloadEvent instance.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PRE_FILE_DOWNLOAD = 'pre-file-download';
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Plugin;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* Plugin interface
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* Version number of the fake composer-plugin-api package
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PLUGIN_API_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* Apply plugin modifications to composer
|
||||
*
|
||||
* @param Composer $composer
|
||||
* @param IOInterface $io
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io);
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Plugin;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\Package;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||
use Composer\DependencyResolver\Pool;
|
||||
|
||||
/**
|
||||
* Plugin manager
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class PluginManager
|
||||
{
|
||||
protected $composer;
|
||||
protected $io;
|
||||
protected $globalRepository;
|
||||
protected $versionParser;
|
||||
|
||||
protected $plugins = array();
|
||||
|
||||
private static $classCounter = 0;
|
||||
|
||||
/**
|
||||
* Initializes plugin manager
|
||||
*
|
||||
* @param Composer $composer
|
||||
* @param IOInterface $io
|
||||
* @param RepositoryInterface $globalRepository
|
||||
*/
|
||||
public function __construct(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
$this->globalRepository = $globalRepository;
|
||||
$this->versionParser = new VersionParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all plugins from currently installed plugin packages
|
||||
*/
|
||||
public function loadInstalledPlugins()
|
||||
{
|
||||
$repo = $this->composer->getRepositoryManager()->getLocalRepository();
|
||||
|
||||
if ($repo) {
|
||||
$this->loadRepository($repo);
|
||||
}
|
||||
if ($this->globalRepository) {
|
||||
$this->loadRepository($this->globalRepository);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin, activates it and registers it with the event dispatcher
|
||||
*
|
||||
* @param PluginInterface $plugin plugin instance
|
||||
*/
|
||||
public function addPlugin(PluginInterface $plugin)
|
||||
{
|
||||
$this->plugins[] = $plugin;
|
||||
$plugin->activate($this->composer, $this->io);
|
||||
|
||||
if ($plugin instanceof EventSubscriberInterface) {
|
||||
$this->composer->getEventDispatcher()->addSubscriber($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all currently active plugin instances
|
||||
*
|
||||
* @return array plugins
|
||||
*/
|
||||
public function getPlugins()
|
||||
{
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all plugins and installers from a repository
|
||||
*
|
||||
* Note that plugins in the specified repository that rely on events that
|
||||
* have fired prior to loading will be missed. This means you likely want to
|
||||
* call this method as early as possible.
|
||||
*
|
||||
* @param RepositoryInterface $repo Repository to scan for plugins to install
|
||||
*/
|
||||
public function loadRepository(RepositoryInterface $repo)
|
||||
{
|
||||
foreach ($repo->getPackages() as $package) {
|
||||
if ($package instanceof AliasPackage) {
|
||||
continue;
|
||||
}
|
||||
if ('composer-plugin' === $package->getType()) {
|
||||
$requiresComposer = null;
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if ($link->getTarget() == 'composer-plugin-api') {
|
||||
$requiresComposer = $link->getConstraint();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$requiresComposer) {
|
||||
throw new \RuntimeException("Plugin ".$package->getName()." is missing a require statement for a version of the composer-plugin-api package.");
|
||||
}
|
||||
|
||||
if (!$requiresComposer->matches(new VersionConstraint('==', $this->versionParser->normalize(PluginInterface::PLUGIN_API_VERSION)))) {
|
||||
$this->io->write("<warning>The plugin ".$package->getName()." requires a version of composer-plugin-api that does not match your composer installation. You may need to run composer update with the '--no-plugins' option.</warning>");
|
||||
}
|
||||
|
||||
$this->registerPackage($package);
|
||||
}
|
||||
// Backward compatibility
|
||||
if ('composer-installer' === $package->getType()) {
|
||||
$this->registerPackage($package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively generates a map of package names to packages for all deps
|
||||
*
|
||||
* @param Pool $pool Package pool of installed packages
|
||||
* @param array $collected Current state of the map for recursion
|
||||
* @param PackageInterface $package The package to analyze
|
||||
*
|
||||
* @return array Map of package names to packages
|
||||
*/
|
||||
protected function collectDependencies(Pool $pool, array $collected, PackageInterface $package)
|
||||
{
|
||||
$requires = array_merge(
|
||||
$package->getRequires(),
|
||||
$package->getDevRequires()
|
||||
);
|
||||
|
||||
foreach ($requires as $requireLink) {
|
||||
$requiredPackage = $this->lookupInstalledPackage($pool, $requireLink);
|
||||
if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) {
|
||||
$collected[$requiredPackage->getName()] = $requiredPackage;
|
||||
$collected = $this->collectDependencies($pool, $collected, $requiredPackage);
|
||||
}
|
||||
}
|
||||
|
||||
return $collected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a package link to a package in the installed pool
|
||||
*
|
||||
* Since dependencies are already installed this should always find one.
|
||||
*
|
||||
* @param Pool $pool Pool of installed packages only
|
||||
* @param Link $link Package link to look up
|
||||
*
|
||||
* @return PackageInterface|null The found package
|
||||
*/
|
||||
protected function lookupInstalledPackage(Pool $pool, Link $link)
|
||||
{
|
||||
$packages = $pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
return (!empty($packages)) ? $packages[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a plugin package, activate it etc.
|
||||
*
|
||||
* If it's of type composer-installer it is registered as an installer
|
||||
* instead for BC
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
*/
|
||||
public function registerPackage(PackageInterface $package)
|
||||
{
|
||||
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
|
||||
|
||||
$extra = $package->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
$classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
|
||||
|
||||
$pool = new Pool('dev');
|
||||
$localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
|
||||
$pool->addRepository($localRepo);
|
||||
if ($this->globalRepository) {
|
||||
$pool->addRepository($this->globalRepository);
|
||||
}
|
||||
|
||||
$autoloadPackages = array($package->getName() => $package);
|
||||
$autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package);
|
||||
|
||||
$generator = $this->composer->getAutoloadGenerator();
|
||||
$autoloads = array();
|
||||
foreach ($autoloadPackages as $autoloadPackage) {
|
||||
$downloadPath = $this->getInstallPath($autoloadPackage, ($this->globalRepository && $this->globalRepository->hasPackage($autoloadPackage)));
|
||||
$autoloads[] = array($autoloadPackage, $downloadPath);
|
||||
}
|
||||
|
||||
$map = $generator->parseAutoloads($autoloads, new Package('dummy', '1.0.0.0', '1.0.0'));
|
||||
$classLoader = $generator->createLoader($map);
|
||||
$classLoader->register();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if (class_exists($class, false)) {
|
||||
$code = file_get_contents($classLoader->findFile($class));
|
||||
$code = preg_replace('{^(\s*)class\s+(\S+)}mi', '$1class $2_composer_tmp'.self::$classCounter, $code);
|
||||
eval('?>'.$code);
|
||||
$class .= '_composer_tmp'.self::$classCounter;
|
||||
self::$classCounter++;
|
||||
}
|
||||
|
||||
if ($oldInstallerPlugin) {
|
||||
$installer = new $class($this->io, $this->composer);
|
||||
$this->composer->getInstallationManager()->addInstaller($installer);
|
||||
} else {
|
||||
$plugin = new $class();
|
||||
$this->addPlugin($plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the path a package is installed to.
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @param bool $global Whether this is a global package
|
||||
*
|
||||
* @return string Install path
|
||||
*/
|
||||
public function getInstallPath(PackageInterface $package, $global = false)
|
||||
{
|
||||
if (!$global) {
|
||||
return $this->composer->getInstallationManager()->getInstallPath($package);
|
||||
}
|
||||
|
||||
$targetDir = $package->getTargetDir();
|
||||
$vendorDir = $this->composer->getConfig()->get('home').'/vendor';
|
||||
|
||||
return ($vendorDir ? $vendorDir.'/' : '').$package->getPrettyName().($targetDir ? '/'.$targetDir : '');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Plugin;
|
||||
|
||||
use Composer\EventDispatcher\Event;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
|
||||
/**
|
||||
* The pre file download event.
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class PreFileDownloadEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var RemoteFilesystem
|
||||
*/
|
||||
private $rfs;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $processedUrl;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param RemoteFilesystem $rfs
|
||||
* @param string $processedUrl
|
||||
*/
|
||||
public function __construct($name, RemoteFilesystem $rfs, $processedUrl)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->rfs = $rfs;
|
||||
$this->processedUrl = $processedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remote filesystem
|
||||
*
|
||||
* @return RemoteFilesystem
|
||||
*/
|
||||
public function getRemoteFilesystem()
|
||||
{
|
||||
return $this->rfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the remote filesystem
|
||||
*
|
||||
* @param RemoteFilesystem $rfs
|
||||
*/
|
||||
public function setRemoteFilesystem(RemoteFilesystem $rfs)
|
||||
{
|
||||
$this->rfs = $rfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the processed URL this remote filesystem will be used for
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProcessedUrl()
|
||||
{
|
||||
return $this->processedUrl;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,9 @@ use Composer\Cache;
|
|||
use Composer\Config;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PreFileDownloadEvent;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -46,12 +49,13 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
|
|||
protected $loader;
|
||||
protected $rootAliases;
|
||||
protected $allowSslDowngrade = false;
|
||||
protected $eventDispatcher;
|
||||
private $rawData;
|
||||
private $minimalPackages;
|
||||
private $degradedMode = false;
|
||||
private $rootData;
|
||||
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config)
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
if (!preg_match('{^[\w.]+\??://}', $repoConfig['url'])) {
|
||||
// assume http as the default protocol
|
||||
|
@ -64,7 +68,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
|
|||
}
|
||||
|
||||
$urlBits = parse_url($repoConfig['url']);
|
||||
if (empty($urlBits['scheme']) || empty($urlBits['host'])) {
|
||||
if ($urlBits === false || empty($urlBits['scheme'])) {
|
||||
throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']);
|
||||
}
|
||||
|
||||
|
@ -83,6 +87,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
|
|||
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$');
|
||||
$this->loader = new ArrayLoader();
|
||||
$this->rfs = new RemoteFilesystem($this->io, $this->options);
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function setRootAliases(array $rootAliases)
|
||||
|
@ -559,7 +564,11 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
|
|||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
$json = $this->rfs->getContents($filename, $filename, false);
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $filename);
|
||||
if ($this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
}
|
||||
$json = $preFileDownloadEvent->getRemoteFilesystem()->getContents($filename, $filename, false);
|
||||
if ($sha256 && $sha256 !== hash('sha256', $json)) {
|
||||
if ($retries) {
|
||||
usleep(100000);
|
||||
|
|
|
@ -17,6 +17,7 @@ use Composer\Package\Version\VersionParser;
|
|||
use Composer\Repository\Pear\ChannelReader;
|
||||
use Composer\Package\CompletePackage;
|
||||
use Composer\Repository\Pear\ChannelInfo;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
|
@ -43,7 +44,7 @@ class PearRepository extends ArrayRepository
|
|||
*/
|
||||
private $vendorAlias;
|
||||
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, RemoteFilesystem $rfs = null)
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, RemoteFilesystem $rfs = null)
|
||||
{
|
||||
if (!preg_match('{^https?://}', $repoConfig['url'])) {
|
||||
$repoConfig['url'] = 'http://'.$repoConfig['url'];
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Repository;
|
|||
|
||||
use Composer\Package\CompletePackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -28,6 +29,12 @@ class PlatformRepository extends ArrayRepository
|
|||
|
||||
$versionParser = new VersionParser();
|
||||
|
||||
$prettyVersion = PluginInterface::PLUGIN_API_VERSION;
|
||||
$version = $versionParser->normalize($prettyVersion);
|
||||
$composerPluginApi = new CompletePackage('composer-plugin-api', $version, $prettyVersion);
|
||||
$composerPluginApi->setDescription('The Composer Plugin API');
|
||||
parent::addPackage($composerPluginApi);
|
||||
|
||||
try {
|
||||
$prettyVersion = PHP_VERSION;
|
||||
$version = $versionParser->normalize($prettyVersion);
|
||||
|
@ -63,7 +70,8 @@ class PlatformRepository extends ArrayRepository
|
|||
$version = $versionParser->normalize($prettyVersion);
|
||||
}
|
||||
|
||||
$ext = new CompletePackage('ext-'.$name, $version, $prettyVersion);
|
||||
$packageName = $this->buildPackageName($name);
|
||||
$ext = new CompletePackage($packageName, $version, $prettyVersion);
|
||||
$ext->setDescription('The '.$name.' PHP extension');
|
||||
parent::addPackage($ext);
|
||||
}
|
||||
|
@ -137,5 +145,25 @@ class PlatformRepository extends ArrayRepository
|
|||
$lib->setDescription('The '.$name.' PHP library');
|
||||
parent::addPackage($lib);
|
||||
}
|
||||
|
||||
if (defined('HPHP_VERSION')) {
|
||||
try {
|
||||
$prettyVersion = HPHP_VERSION;
|
||||
$version = $versionParser->normalize($prettyVersion);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
$prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', HPHP_VERSION);
|
||||
$version = $versionParser->normalize($prettyVersion);
|
||||
}
|
||||
|
||||
$hhvm = new CompletePackage('hhvm', $version, $prettyVersion);
|
||||
$hhvm->setDescription('The HHVM Runtime (64bit)');
|
||||
parent::addPackage($hhvm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function buildPackageName($name)
|
||||
{
|
||||
return 'ext-' . str_replace(' ', '-', $name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Repository;
|
|||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Config;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Repositories manager.
|
||||
|
@ -29,11 +30,13 @@ class RepositoryManager
|
|||
private $repositoryClasses = array();
|
||||
private $io;
|
||||
private $config;
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config)
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,7 +101,7 @@ class RepositoryManager
|
|||
|
||||
$class = $this->repositoryClasses[$type];
|
||||
|
||||
return new $class($config, $this->io, $this->config);
|
||||
return new $class($config, $this->io, $this->config, $this->eventDispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
|
@ -140,7 +141,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if (!preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) {
|
||||
return false;
|
||||
|
|
|
@ -17,12 +17,15 @@ use Composer\Util\ProcessExecutor;
|
|||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Git as GitUtil;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Cache;
|
||||
use Composer\Config;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class GitDriver extends VcsDriver
|
||||
{
|
||||
protected $cache;
|
||||
protected $tags;
|
||||
protected $branches;
|
||||
protected $rootIdentifier;
|
||||
|
@ -77,6 +80,8 @@ class GitDriver extends VcsDriver
|
|||
|
||||
$this->getTags();
|
||||
$this->getBranches();
|
||||
|
||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,6 +137,10 @@ class GitDriver extends VcsDriver
|
|||
*/
|
||||
public function getComposerInformation($identifier)
|
||||
{
|
||||
if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
|
||||
$this->infoCache[$identifier] = JsonFile::parseJson($res);
|
||||
}
|
||||
|
||||
if (!isset($this->infoCache[$identifier])) {
|
||||
$resource = sprintf('%s:composer.json', escapeshellarg($identifier));
|
||||
$this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir);
|
||||
|
@ -147,6 +156,11 @@ class GitDriver extends VcsDriver
|
|||
$date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
|
||||
$composer['time'] = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
|
||||
$this->cache->write($identifier, json_encode($composer));
|
||||
}
|
||||
|
||||
$this->infoCache[$identifier] = $composer;
|
||||
}
|
||||
|
||||
|
@ -198,7 +212,7 @@ class GitDriver extends VcsDriver
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if (preg_match('#(^git://|\.git$|git(?:olite)?@|//git\.|//github.com/)#i', $url)) {
|
||||
return true;
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\Downloader\TransportException;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Cache;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\GitHub;
|
||||
|
||||
/**
|
||||
|
@ -46,10 +46,10 @@ class GitHubDriver extends VcsDriver
|
|||
*/
|
||||
public function initialize()
|
||||
{
|
||||
preg_match('#^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match);
|
||||
$this->owner = $match[1];
|
||||
$this->repository = $match[2];
|
||||
$this->originUrl = 'github.com';
|
||||
preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match);
|
||||
$this->owner = $match[3];
|
||||
$this->repository = $match[4];
|
||||
$this->originUrl = !empty($match[1]) ? $match[1] : $match[2];
|
||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
|
||||
|
||||
$this->fetchRootIdentifier();
|
||||
|
@ -76,7 +76,21 @@ class GitHubDriver extends VcsDriver
|
|||
return $this->gitDriver->getUrl();
|
||||
}
|
||||
|
||||
return 'https://github.com/'.$this->owner.'/'.$this->repository.'.git';
|
||||
return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getApiUrl()
|
||||
{
|
||||
if ('github.com' === $this->originUrl) {
|
||||
$apiUrl = 'api.github.com';
|
||||
} else {
|
||||
$apiUrl = $this->originUrl . '/api/v3';
|
||||
}
|
||||
|
||||
return 'https://' . $apiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +120,8 @@ class GitHubDriver extends VcsDriver
|
|||
if ($this->gitDriver) {
|
||||
return $this->gitDriver->getDist($identifier);
|
||||
}
|
||||
$url = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/zipball/'.$identifier;
|
||||
|
||||
$url = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/zipball/'.$identifier;
|
||||
|
||||
return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
|
||||
}
|
||||
|
@ -128,7 +143,7 @@ class GitHubDriver extends VcsDriver
|
|||
$notFoundRetries = 2;
|
||||
while ($notFoundRetries) {
|
||||
try {
|
||||
$resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/contents/composer.json?ref='.urlencode($identifier);
|
||||
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/composer.json?ref='.urlencode($identifier);
|
||||
$composer = JsonFile::parseJson($this->getContents($resource));
|
||||
if (empty($composer['content']) || $composer['encoding'] !== 'base64' || !($composer = base64_decode($composer['content']))) {
|
||||
throw new \RuntimeException('Could not retrieve composer.json from '.$resource);
|
||||
|
@ -150,16 +165,16 @@ class GitHubDriver extends VcsDriver
|
|||
$composer = JsonFile::parseJson($composer, $resource);
|
||||
|
||||
if (!isset($composer['time'])) {
|
||||
$resource = 'https://api.github.com/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);
|
||||
$composer['time'] = $commit['commit']['committer']['date'];
|
||||
}
|
||||
if (!isset($composer['support']['source'])) {
|
||||
$label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
|
||||
$composer['support']['source'] = sprintf('https://github.com/%s/%s/tree/%s', $this->owner, $this->repository, $label);
|
||||
$composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
|
||||
}
|
||||
if (!isset($composer['support']['issues']) && $this->hasIssues) {
|
||||
$composer['support']['issues'] = sprintf('https://github.com/%s/%s/issues', $this->owner, $this->repository);
|
||||
$composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +197,7 @@ class GitHubDriver extends VcsDriver
|
|||
return $this->gitDriver->getTags();
|
||||
}
|
||||
if (null === $this->tags) {
|
||||
$resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags';
|
||||
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags';
|
||||
$tagsData = JsonFile::parseJson($this->getContents($resource), $resource);
|
||||
$this->tags = array();
|
||||
foreach ($tagsData as $tag) {
|
||||
|
@ -202,7 +217,7 @@ class GitHubDriver extends VcsDriver
|
|||
return $this->gitDriver->getBranches();
|
||||
}
|
||||
if (null === $this->branches) {
|
||||
$resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads';
|
||||
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads';
|
||||
$branchData = JsonFile::parseJson($this->getContents($resource), $resource);
|
||||
$this->branches = array();
|
||||
foreach ($branchData as $branch) {
|
||||
|
@ -217,9 +232,14 @@ class GitHubDriver extends VcsDriver
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if (!preg_match('#^((?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$#', $url)) {
|
||||
if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git)?$#', $url, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$originUrl = !empty($matches[2]) ? $matches[2] : $matches[3];
|
||||
if (!in_array($originUrl, $config->get('github-domains'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -241,7 +261,7 @@ class GitHubDriver extends VcsDriver
|
|||
*/
|
||||
protected function generateSshUrl()
|
||||
{
|
||||
return 'git@github.com:'.$this->owner.'/'.$this->repository.'.git';
|
||||
return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,7 +378,7 @@ class GitHubDriver extends VcsDriver
|
|||
*/
|
||||
protected function fetchRootIdentifier()
|
||||
{
|
||||
$repoDataUrl = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository;
|
||||
$repoDataUrl = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository;
|
||||
|
||||
$repoData = JsonFile::parseJson($this->getContents($repoDataUrl, true), $repoDataUrl);
|
||||
if (null === $repoData && null !== $this->gitDriver) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
|
@ -150,7 +151,7 @@ class HgBitbucketDriver extends VcsDriver
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if (!preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) {
|
||||
return false;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Filesystem;
|
||||
|
@ -48,14 +49,14 @@ class HgDriver extends VcsDriver
|
|||
|
||||
// update the repo if it is a valid hg repository
|
||||
if (is_dir($this->repoDir) && 0 === $this->process->execute('hg summary', $output, $this->repoDir)) {
|
||||
if (0 !== $this->process->execute('hg pull -u', $output, $this->repoDir)) {
|
||||
if (0 !== $this->process->execute('hg pull', $output, $this->repoDir)) {
|
||||
$this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')</error>');
|
||||
}
|
||||
} else {
|
||||
// clean up directory and do a fresh clone into it
|
||||
$fs->removeDirectory($this->repoDir);
|
||||
|
||||
if (0 !== $this->process->execute(sprintf('hg clone %s %s', escapeshellarg($this->url), escapeshellarg($this->repoDir)), $output, $cacheDir)) {
|
||||
if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', escapeshellarg($this->url), escapeshellarg($this->repoDir)), $output, $cacheDir)) {
|
||||
$output = $this->process->getErrorOutput();
|
||||
|
||||
if (0 !== $this->process->execute('hg --version', $ignoredOutput)) {
|
||||
|
@ -124,7 +125,7 @@ class HgDriver extends VcsDriver
|
|||
$composer = JsonFile::parseJson($composer, $identifier);
|
||||
|
||||
if (!isset($composer['time'])) {
|
||||
$this->process->execute(sprintf('hg log --template "{date|rfc822date}" -r %s', escapeshellarg($identifier)), $output, $this->repoDir);
|
||||
$this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', escapeshellarg($identifier)), $output, $this->repoDir);
|
||||
$date = new \DateTime(trim($output), new \DateTimeZone('UTC'));
|
||||
$composer['time'] = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
@ -189,7 +190,7 @@ class HgDriver extends VcsDriver
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if (preg_match('#(^(?:https?|ssh)://(?:[^@]@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i', $url)) {
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Perforce;
|
||||
|
||||
/**
|
||||
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||
*/
|
||||
class PerforceDriver extends VcsDriver
|
||||
{
|
||||
protected $depot;
|
||||
protected $branch;
|
||||
protected $perforce;
|
||||
protected $composerInfo;
|
||||
protected $composerInfoIdentifier;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->depot = $this->repoConfig['depot'];
|
||||
$this->branch = '';
|
||||
if (isset($this->repoConfig['branch'])) {
|
||||
$this->branch = $this->repoConfig['branch'];
|
||||
}
|
||||
|
||||
$this->initPerforce($this->repoConfig);
|
||||
$this->perforce->p4Login($this->io);
|
||||
$this->perforce->checkStream($this->depot);
|
||||
|
||||
$this->perforce->writeP4ClientSpec();
|
||||
$this->perforce->connectClient();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function initPerforce($repoConfig)
|
||||
{
|
||||
if (isset($this->perforce)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot;
|
||||
$this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getComposerInformation($identifier)
|
||||
{
|
||||
if (isset($this->composerInfoIdentifier)) {
|
||||
if (strcmp($identifier, $this->composerInfoIdentifier) === 0) {
|
||||
return $this->composerInfo;
|
||||
}
|
||||
}
|
||||
$composer_info = $this->perforce->getComposerInformation($identifier);
|
||||
|
||||
return $composer_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getRootIdentifier()
|
||||
{
|
||||
return $this->branch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getBranches()
|
||||
{
|
||||
$branches = $this->perforce->getBranches();
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
$tags = $this->perforce->getTags();
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDist($identifier)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSource($identifier)
|
||||
{
|
||||
$source = array(
|
||||
'type' => 'perforce',
|
||||
'url' => $this->repoConfig['url'],
|
||||
'reference' => $identifier,
|
||||
'p4user' => $this->perforce->getUser()
|
||||
);
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasComposerFile($identifier)
|
||||
{
|
||||
$this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
|
||||
$this->composerInfoIdentifier = $identifier;
|
||||
$result = false;
|
||||
if (isset($this->composerInfo)) {
|
||||
$result = count($this->composerInfo) > 0;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getContents($url)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
if ($deep || preg_match('#\b(perforce|p4)\b#i', $url)) {
|
||||
return Perforce::checkServerExists($url, new ProcessExecutor($io));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup()
|
||||
{
|
||||
$this->perforce->cleanupClientSpec();
|
||||
$this->perforce = null;
|
||||
}
|
||||
|
||||
public function getDepot()
|
||||
{
|
||||
return $this->depot;
|
||||
}
|
||||
|
||||
public function getBranch()
|
||||
{
|
||||
return $this->branch;
|
||||
}
|
||||
|
||||
public function setPerforce(Perforce $perforce)
|
||||
{
|
||||
$this->perforce = $perforce;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Cache;
|
||||
use Composer\Config;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Filesystem;
|
||||
|
@ -193,7 +194,13 @@ class SvnDriver extends VcsDriver
|
|||
if (null === $this->branches) {
|
||||
$this->branches = array();
|
||||
|
||||
$output = $this->execute('svn ls --verbose', $this->baseUrl . '/');
|
||||
if (false === strpos($this->trunkPath, '/')) {
|
||||
$trunkParent = $this->baseUrl . '/';
|
||||
} else {
|
||||
$trunkParent = $this->baseUrl . '/' . dirname($this->trunkPath) . '/';
|
||||
}
|
||||
|
||||
$output = $this->execute('svn ls --verbose', $trunkParent);
|
||||
if ($output) {
|
||||
foreach ($this->process->splitLines($output) as $line) {
|
||||
$line = trim($line);
|
||||
|
@ -235,7 +242,7 @@ class SvnDriver extends VcsDriver
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false)
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||
{
|
||||
$url = self::normalizeUrl($url);
|
||||
if (preg_match('#(^svn://|^svn\+ssh://|svn\.)#i', $url)) {
|
||||
|
|
|
@ -101,8 +101,22 @@ abstract class VcsDriver implements VcsDriverInterface
|
|||
return $this->remoteFilesystem->getContents($this->originUrl, $url, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if current repository url is local
|
||||
*
|
||||
* @param string $url
|
||||
* @return boolean Repository url is local
|
||||
*/
|
||||
protected static function isLocalUrl($url)
|
||||
{
|
||||
return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Repository\Vcs;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
|
@ -81,13 +82,20 @@ interface VcsDriverInterface
|
|||
*/
|
||||
public function hasComposerFile($identifier);
|
||||
|
||||
/**
|
||||
* Performs any cleanup necessary as the driver is not longer needed
|
||||
*
|
||||
*/
|
||||
public function cleanup();
|
||||
|
||||
/**
|
||||
* Checks if this driver can handle a given url
|
||||
*
|
||||
* @param IOInterface $io IO instance
|
||||
* @param string $url
|
||||
* @param bool $deep unless true, only shallow checks (url matching typically) should be done
|
||||
* @param IOInterface $io IO instance
|
||||
* @param Config $config current $config
|
||||
* @param string $url URL to validate/check
|
||||
* @param bool $deep unless true, only shallow checks (url matching typically) should be done
|
||||
* @return bool
|
||||
*/
|
||||
public static function supports(IOInterface $io, $url, $deep = false);
|
||||
public static function supports(IOInterface $io, Config $config, $url, $deep = false);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use Composer\Package\Loader\ArrayLoader;
|
|||
use Composer\Package\Loader\ValidatingArrayLoader;
|
||||
use Composer\Package\Loader\InvalidPackageException;
|
||||
use Composer\Package\Loader\LoaderInterface;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Config;
|
||||
|
||||
|
@ -38,7 +39,7 @@ class VcsRepository extends ArrayRepository
|
|||
protected $repoConfig;
|
||||
protected $branchErrorOccurred = false;
|
||||
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, array $drivers = null)
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, array $drivers = null)
|
||||
{
|
||||
$this->drivers = $drivers ?: array(
|
||||
'github' => 'Composer\Repository\Vcs\GitHubDriver',
|
||||
|
@ -46,6 +47,7 @@ class VcsRepository extends ArrayRepository
|
|||
'git' => 'Composer\Repository\Vcs\GitDriver',
|
||||
'hg-bitbucket' => 'Composer\Repository\Vcs\HgBitbucketDriver',
|
||||
'hg' => 'Composer\Repository\Vcs\HgDriver',
|
||||
'perforce' => 'Composer\Repository\Vcs\PerforceDriver',
|
||||
// svn must be last because identifying a subversion server for sure is practically impossible
|
||||
'svn' => 'Composer\Repository\Vcs\SvnDriver',
|
||||
);
|
||||
|
@ -58,6 +60,11 @@ class VcsRepository extends ArrayRepository
|
|||
$this->repoConfig = $repoConfig;
|
||||
}
|
||||
|
||||
public function getRepoConfig()
|
||||
{
|
||||
return $this->repoConfig;
|
||||
}
|
||||
|
||||
public function setLoader(LoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
|
@ -74,7 +81,7 @@ class VcsRepository extends ArrayRepository
|
|||
}
|
||||
|
||||
foreach ($this->drivers as $driver) {
|
||||
if ($driver::supports($this->io, $this->url)) {
|
||||
if ($driver::supports($this->io, $this->config, $this->url)) {
|
||||
$driver = new $driver($this->repoConfig, $this->io, $this->config);
|
||||
$driver->initialize();
|
||||
|
||||
|
@ -83,7 +90,7 @@ class VcsRepository extends ArrayRepository
|
|||
}
|
||||
|
||||
foreach ($this->drivers as $driver) {
|
||||
if ($driver::supports($this->io, $this->url, true)) {
|
||||
if ($driver::supports($this->io, $this->config, $this->url, true)) {
|
||||
$driver = new $driver($this->repoConfig, $this->io, $this->config);
|
||||
$driver->initialize();
|
||||
|
||||
|
@ -247,6 +254,7 @@ class VcsRepository extends ArrayRepository
|
|||
continue;
|
||||
}
|
||||
}
|
||||
$driver->cleanup();
|
||||
|
||||
if (!$verbose) {
|
||||
$this->io->overwrite('', false);
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
namespace Composer\Script;
|
||||
|
||||
use Composer\Composer;
|
||||
|
||||
/**
|
||||
* The Command Event.
|
||||
*
|
||||
|
|
|
@ -16,17 +16,13 @@ use Composer\Composer;
|
|||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* The base event class
|
||||
* The script event class
|
||||
*
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class Event
|
||||
class Event extends \Composer\EventDispatcher\Event
|
||||
{
|
||||
/**
|
||||
* @var string This event's name
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var Composer The composer instance
|
||||
*/
|
||||
|
@ -52,22 +48,12 @@ class Event
|
|||
*/
|
||||
public function __construct($name, Composer $composer, IOInterface $io, $devMode = false)
|
||||
{
|
||||
$this->name = $name;
|
||||
parent::__construct($name);
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
$this->devMode = $devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event's name.
|
||||
*
|
||||
* @return string The event name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the composer instance.
|
||||
*
|
||||
|
|
|
@ -104,6 +104,15 @@ class ConfigValidator
|
|||
);
|
||||
}
|
||||
|
||||
if (!empty($manifest['type']) && $manifest['type'] == 'composer-installer') {
|
||||
$warnings[] = "The package type 'composer-installer' is deprecated. Please distribute your custom installers as plugins from now on. See http://getcomposer.org/doc/articles/plugins.md for plugin documentation.";
|
||||
}
|
||||
|
||||
$requireOverrides = array_intersect_key($manifest['require'], $manifest['require-dev']);
|
||||
if (!empty($requireOverrides)) {
|
||||
$warnings[] = implode(', ', array_keys($requireOverrides)). " is required both in require and require-dev, this can lead to unexpected behavior";
|
||||
}
|
||||
|
||||
try {
|
||||
$loader = new ValidatingArrayLoader(new ArrayLoader());
|
||||
if (!isset($manifest['version'])) {
|
||||
|
|
|
@ -41,6 +41,19 @@ class Filesystem
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a directory is empty
|
||||
*
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
*/
|
||||
public function isDirEmpty($dir)
|
||||
{
|
||||
$dir = rtrim($dir, '/\\');
|
||||
|
||||
return count(glob($dir.'/*') ?: array()) === 0 && count(glob($dir.'/.*') ?: array()) === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove a directory
|
||||
*
|
||||
|
@ -56,6 +69,10 @@ class Filesystem
|
|||
return true;
|
||||
}
|
||||
|
||||
if (preg_match('{^(?:[a-z]:)?[/\\\\]+$}i', $directory)) {
|
||||
throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.');
|
||||
}
|
||||
|
||||
if (!function_exists('proc_open')) {
|
||||
return $this->removeDirectoryPhp($directory);
|
||||
}
|
||||
|
@ -129,15 +146,12 @@ class Filesystem
|
|||
{
|
||||
$it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
if (!file_exists($target)) {
|
||||
mkdir($target, 0777, true);
|
||||
}
|
||||
$this->ensureDirectoryExists($target);
|
||||
|
||||
foreach ($ri as $file) {
|
||||
$targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
|
||||
if ($file->isDir()) {
|
||||
mkdir($targetPath);
|
||||
$this->ensureDirectoryExists($targetPath);
|
||||
} else {
|
||||
copy($file->getPathname(), $targetPath);
|
||||
}
|
||||
|
@ -212,12 +226,12 @@ class Filesystem
|
|||
return './'.basename($to);
|
||||
}
|
||||
|
||||
$commonPath = $to.'/';
|
||||
while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) {
|
||||
$commonPath = $to;
|
||||
while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) {
|
||||
$commonPath = strtr(dirname($commonPath), '\\', '/');
|
||||
}
|
||||
|
||||
if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) {
|
||||
if (0 !== strpos($from, $commonPath) || '/' === $commonPath) {
|
||||
return $to;
|
||||
}
|
||||
|
||||
|
@ -250,8 +264,8 @@ class Filesystem
|
|||
return $directories ? '__DIR__' : '__FILE__';
|
||||
}
|
||||
|
||||
$commonPath = $to.'/';
|
||||
while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) {
|
||||
$commonPath = $to;
|
||||
while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) {
|
||||
$commonPath = strtr(dirname($commonPath), '\\', '/');
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
namespace Composer\Util;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
|
|
|
@ -51,7 +51,7 @@ class GitHub
|
|||
*/
|
||||
public function authorizeOAuth($originUrl)
|
||||
{
|
||||
if ('github.com' !== $originUrl) {
|
||||
if (!in_array($originUrl, $this->config->get('github-domains'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,8 @@ class GitHub
|
|||
{
|
||||
$attemptCounter = 0;
|
||||
|
||||
$apiUrl = ('github.com' === $originUrl) ? 'api.github.com' : $originUrl . '/api/v3';
|
||||
|
||||
if ($message) {
|
||||
$this->io->write($message);
|
||||
}
|
||||
|
@ -95,7 +97,7 @@ class GitHub
|
|||
$appName .= ' on ' . trim($output);
|
||||
}
|
||||
|
||||
$contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://api.github.com/authorizations', false, array(
|
||||
$contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'follow_location' => false,
|
||||
|
|
|
@ -73,7 +73,14 @@ class NoProxyPattern
|
|||
if (strpos($ruleHost, '/') === false) {
|
||||
$match = $ip === $ruleHost;
|
||||
} else {
|
||||
$match = self::inCIDRBlock($ruleHost, $ip);
|
||||
// gethostbyname() failed to resolve $host to an ip, so we assume
|
||||
// it must be proxied to let the proxy's DNS resolve it
|
||||
if ($ip === $host) {
|
||||
$match = false;
|
||||
} else {
|
||||
// match resolved IP against the rule
|
||||
$match = self::inCIDRBlock($ruleHost, $ip);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// match end of domain
|
||||
|
@ -100,12 +107,12 @@ class NoProxyPattern
|
|||
}
|
||||
|
||||
/**
|
||||
* Check an IP adress against a CIDR
|
||||
* Check an IP address against a CIDR
|
||||
*
|
||||
* http://framework.zend.com/svn/framework/extras/incubator/library/ZendX/Whois/Adapter/Cidr.php
|
||||
*
|
||||
* @param string $cidr IPv4 block in CIDR notation
|
||||
* @param string $ip IPv4 address
|
||||
* @param string $ip IPv4 address
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
|
@ -134,7 +141,7 @@ class NoProxyPattern
|
|||
$check = ($a << 24) + ($b << 16) + ($c << 8) + $d;
|
||||
|
||||
// If the ip is within the range, including highest/lowest values,
|
||||
// then it's witin the CIDR range
|
||||
// then it's within the CIDR range
|
||||
return $check >= $low && $check <= $high;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Util;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||
*/
|
||||
class Perforce
|
||||
{
|
||||
protected $path;
|
||||
protected $p4Depot;
|
||||
protected $p4Client;
|
||||
protected $p4User;
|
||||
protected $p4Password;
|
||||
protected $p4Port;
|
||||
protected $p4Stream;
|
||||
protected $p4ClientSpec;
|
||||
protected $p4DepotType;
|
||||
protected $p4Branch;
|
||||
protected $process;
|
||||
protected $uniquePerforceClientName;
|
||||
protected $windowsFlag;
|
||||
protected $commandResult;
|
||||
|
||||
public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows)
|
||||
{
|
||||
$this->windowsFlag = $isWindows;
|
||||
$this->p4Port = $port;
|
||||
$this->initializePath($path);
|
||||
$this->process = $process;
|
||||
$this->initialize($repoConfig);
|
||||
}
|
||||
|
||||
public static function create($repoConfig, $port, $path, ProcessExecutor $process = null)
|
||||
{
|
||||
if (!isset($process)) {
|
||||
$process = new ProcessExecutor;
|
||||
}
|
||||
$isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
|
||||
|
||||
$perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows);
|
||||
|
||||
return $perforce;
|
||||
}
|
||||
|
||||
public function initialize($repoConfig)
|
||||
{
|
||||
$this->uniquePerforceClientName = $this->generateUniquePerforceClientName();
|
||||
if (null == $repoConfig) {
|
||||
return;
|
||||
}
|
||||
if (isset($repoConfig['unique_perforce_client_name'])) {
|
||||
$this->uniquePerforceClientName = $repoConfig['unique_perforce_client_name'];
|
||||
}
|
||||
|
||||
if (isset($repoConfig['depot'])) {
|
||||
$this->p4Depot = $repoConfig['depot'];
|
||||
}
|
||||
if (isset($repoConfig['branch'])) {
|
||||
$this->p4Branch = $repoConfig['branch'];
|
||||
}
|
||||
if (isset($repoConfig['p4user'])) {
|
||||
$this->p4User = $repoConfig['p4user'];
|
||||
} else {
|
||||
$this->p4User = $this->getP4variable('P4USER');
|
||||
}
|
||||
if (isset($repoConfig['p4password'])) {
|
||||
$this->p4Password = $repoConfig['p4password'];
|
||||
}
|
||||
}
|
||||
|
||||
public function initializeDepotAndBranch($depot, $branch)
|
||||
{
|
||||
if (isset($depot)) {
|
||||
$this->p4Depot = $depot;
|
||||
}
|
||||
if (isset($branch)) {
|
||||
$this->p4Branch = $branch;
|
||||
}
|
||||
}
|
||||
|
||||
public function generateUniquePerforceClientName()
|
||||
{
|
||||
return gethostname() . "_" . time();
|
||||
}
|
||||
|
||||
public function cleanupClientSpec()
|
||||
{
|
||||
$client = $this->getClient();
|
||||
$command = 'p4 client -d $client';
|
||||
$this->executeCommand($command);
|
||||
$clientSpec = $this->getP4ClientSpec();
|
||||
$fileSystem = new FileSystem($this->process);
|
||||
$fileSystem->remove($clientSpec);
|
||||
}
|
||||
|
||||
protected function executeCommand($command)
|
||||
{
|
||||
$this->commandResult = "";
|
||||
$exit_code = $this->process->execute($command, $this->commandResult);
|
||||
return $exit_code;
|
||||
}
|
||||
|
||||
public function getClient()
|
||||
{
|
||||
if (!isset($this->p4Client)) {
|
||||
$cleanStreamName = str_replace('@', '', str_replace('/', '_', str_replace('//', '', $this->getStream())));
|
||||
$this->p4Client = 'composer_perforce_' . $this->uniquePerforceClientName . '_' . $cleanStreamName;
|
||||
}
|
||||
|
||||
return $this->p4Client;
|
||||
}
|
||||
|
||||
protected function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function initializePath($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
$fs = new Filesystem();
|
||||
$fs->ensureDirectoryExists($path);
|
||||
}
|
||||
|
||||
protected function getPort()
|
||||
{
|
||||
return $this->p4Port;
|
||||
}
|
||||
|
||||
public function setStream($stream)
|
||||
{
|
||||
$this->p4Stream = $stream;
|
||||
$index = strrpos($stream, '/');
|
||||
//Stream format is //depot/stream, while non-streaming depot is //depot
|
||||
if ($index > 2) {
|
||||
$this->p4DepotType = 'stream';
|
||||
}
|
||||
}
|
||||
|
||||
public function isStream()
|
||||
{
|
||||
return (strcmp($this->p4DepotType, 'stream') === 0);
|
||||
}
|
||||
|
||||
public function getStream()
|
||||
{
|
||||
if (!isset($this->p4Stream)) {
|
||||
if ($this->isStream()) {
|
||||
$this->p4Stream = '//' . $this->p4Depot . '/' . $this->p4Branch;
|
||||
} else {
|
||||
$this->p4Stream = '//' . $this->p4Depot;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->p4Stream;
|
||||
}
|
||||
|
||||
public function getStreamWithoutLabel($stream)
|
||||
{
|
||||
$index = strpos($stream, '@');
|
||||
if ($index === false) {
|
||||
return $stream;
|
||||
}
|
||||
|
||||
return substr($stream, 0, $index);
|
||||
}
|
||||
|
||||
public function getP4ClientSpec()
|
||||
{
|
||||
$p4clientSpec = $this->path . '/' . $this->getClient() . '.p4.spec';
|
||||
|
||||
return $p4clientSpec;
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->p4User;
|
||||
}
|
||||
|
||||
public function queryP4User(IOInterface $io)
|
||||
{
|
||||
$this->getUser();
|
||||
if (strlen($this->p4User) > 0) {
|
||||
return;
|
||||
}
|
||||
$this->p4User = $this->getP4variable('P4USER');
|
||||
if (strlen($this->p4User) > 0) {
|
||||
return;
|
||||
}
|
||||
$this->p4User = $io->ask('Enter P4 User:');
|
||||
if ($this->windowsFlag) {
|
||||
$command = 'p4 set P4USER=' . $this->p4User;
|
||||
} else {
|
||||
$command = 'export P4USER=' . $this->p4User;
|
||||
}
|
||||
$this->executeCommand($command);
|
||||
}
|
||||
|
||||
protected function getP4variable($name)
|
||||
{
|
||||
if ($this->windowsFlag) {
|
||||
$command = 'p4 set';
|
||||
$this->executeCommand($command);
|
||||
$result = trim($this->commandResult);
|
||||
$resArray = explode(PHP_EOL, $result);
|
||||
foreach ($resArray as $line) {
|
||||
$fields = explode('=', $line);
|
||||
if (strcmp($name, $fields[0]) == 0) {
|
||||
$index = strpos($fields[1], ' ');
|
||||
if ($index === false) {
|
||||
$value = $fields[1];
|
||||
} else {
|
||||
$value = substr($fields[1], 0, $index);
|
||||
}
|
||||
$value = trim($value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$command = 'echo $' . $name;
|
||||
$this->executeCommand($command);
|
||||
$result = trim($this->commandResult);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function queryP4Password(IOInterface $io)
|
||||
{
|
||||
if (isset($this->p4Password)) {
|
||||
return $this->p4Password;
|
||||
}
|
||||
$password = $this->getP4variable('P4PASSWD');
|
||||
if (strlen($password) <= 0) {
|
||||
$password = $io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': ');
|
||||
}
|
||||
$this->p4Password = $password;
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
public function generateP4Command($command, $useClient = true)
|
||||
{
|
||||
$p4Command = 'p4 ';
|
||||
$p4Command = $p4Command . '-u ' . $this->getUser() . ' ';
|
||||
if ($useClient) {
|
||||
$p4Command = $p4Command . '-c ' . $this->getClient() . ' ';
|
||||
}
|
||||
$p4Command = $p4Command . '-p ' . $this->getPort() . ' ';
|
||||
$p4Command = $p4Command . $command;
|
||||
|
||||
return $p4Command;
|
||||
}
|
||||
|
||||
public function isLoggedIn()
|
||||
{
|
||||
$command = $this->generateP4Command('login -s', false);
|
||||
$exitCode = $this->executeCommand($command);
|
||||
if ($exitCode){
|
||||
$errorOutput = $this->process->getErrorOutput();
|
||||
$index = strpos($errorOutput, $this->getUser());
|
||||
if ($index === false){
|
||||
$index = strpos($errorOutput, 'p4');
|
||||
if ($index===false){
|
||||
return false;
|
||||
}
|
||||
throw new \Exception('p4 command not found in path: ' . $errorOutput);
|
||||
}
|
||||
throw new \Exception('Invalid user name: ' . $this->getUser() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function connectClient()
|
||||
{
|
||||
$p4CreateClientCommand = $this->generateP4Command('client -i < ' . $this->getP4ClientSpec());
|
||||
$this->executeCommand($p4CreateClientCommand);
|
||||
}
|
||||
|
||||
public function syncCodeBase($label)
|
||||
{
|
||||
$prevDir = getcwd();
|
||||
chdir($this->path);
|
||||
|
||||
$p4SyncCommand = $this->generateP4Command('sync -f ');
|
||||
if (isset($label)) {
|
||||
if (strcmp($label, 'dev-master') != 0) {
|
||||
$p4SyncCommand = $p4SyncCommand . '@' . $label;
|
||||
}
|
||||
}
|
||||
$this->executeCommand($p4SyncCommand);
|
||||
|
||||
chdir($prevDir);
|
||||
}
|
||||
|
||||
public function writeClientSpecToFile($spec)
|
||||
{
|
||||
fwrite($spec, 'Client: ' . $this->getClient() . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'Update: ' . date('Y/m/d H:i:s') . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'Access: ' . date('Y/m/d H:i:s') . PHP_EOL);
|
||||
fwrite($spec, 'Owner: ' . $this->getUser() . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'Description:' . PHP_EOL);
|
||||
fwrite($spec, ' Created by ' . $this->getUser() . ' from composer.' . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'Root: ' . $this->getPath() . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'Options: noallwrite noclobber nocompress unlocked modtime rmdir' . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'SubmitOptions: revertunchanged' . PHP_EOL . PHP_EOL);
|
||||
fwrite($spec, 'LineEnd: local' . PHP_EOL . PHP_EOL);
|
||||
if ($this->isStream()) {
|
||||
fwrite($spec, 'Stream:' . PHP_EOL);
|
||||
fwrite($spec, ' ' . $this->getStreamWithoutLabel($this->p4Stream) . PHP_EOL);
|
||||
} else {
|
||||
fwrite(
|
||||
$spec,
|
||||
'View: ' . $this->getStream() . '/... //' . $this->getClient() . '/... ' . PHP_EOL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function writeP4ClientSpec()
|
||||
{
|
||||
$clientSpec = $this->getP4ClientSpec();
|
||||
$spec = fopen($clientSpec, 'w');
|
||||
try {
|
||||
$this->writeClientSpecToFile($spec);
|
||||
} catch (\Exception $e) {
|
||||
fclose($spec);
|
||||
throw $e;
|
||||
}
|
||||
fclose($spec);
|
||||
}
|
||||
|
||||
protected function read($pipe, $name)
|
||||
{
|
||||
if (feof($pipe)) {
|
||||
return;
|
||||
}
|
||||
$line = fgets($pipe);
|
||||
while ($line != false) {
|
||||
$line = fgets($pipe);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function windowsLogin($password)
|
||||
{
|
||||
$command = $this->generateP4Command(' login -a');
|
||||
$process = new Process($command, null, null, $password);
|
||||
|
||||
return $process->run();
|
||||
}
|
||||
|
||||
public function p4Login(IOInterface $io)
|
||||
{
|
||||
$this->queryP4User($io);
|
||||
if (!$this->isLoggedIn()) {
|
||||
$password = $this->queryP4Password($io);
|
||||
if ($this->windowsFlag) {
|
||||
$this->windowsLogin($password);
|
||||
} else {
|
||||
$command = 'echo ' . $password . ' | ' . $this->generateP4Command(' login -a', false);
|
||||
$exitCode = $this->executeCommand($command);
|
||||
$result = trim($this->commandResult);
|
||||
if ($exitCode){
|
||||
throw new \Exception("Error logging in:" . $this->process->getErrorOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkServerExists($url, ProcessExecutor $processExecutor)
|
||||
{
|
||||
$output = null;
|
||||
return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output);
|
||||
}
|
||||
|
||||
public function getComposerInformation($identifier)
|
||||
{
|
||||
$index = strpos($identifier, '@');
|
||||
if ($index === false) {
|
||||
$composerJson = $identifier. '/composer.json';
|
||||
|
||||
return $this->getComposerInformationFromPath($composerJson);
|
||||
}
|
||||
|
||||
return $this->getComposerInformationFromLabel($identifier, $index);
|
||||
}
|
||||
|
||||
public function getComposerInformationFromPath($composerJson)
|
||||
{
|
||||
$command = $this->generateP4Command(' print ' . $composerJson);
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
$index = strpos($result, '{');
|
||||
if ($index === false) {
|
||||
return '';
|
||||
}
|
||||
if ($index >= 0) {
|
||||
$rawData = substr($result, $index);
|
||||
$composer_info = json_decode($rawData, true);
|
||||
|
||||
return $composer_info;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getComposerInformationFromLabel($identifier, $index)
|
||||
{
|
||||
$composerJsonPath = substr($identifier, 0, $index) . '/composer.json' . substr($identifier, $index);
|
||||
$command = $this->generateP4Command(' files ' . $composerJsonPath, false);
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
$index2 = strpos($result, 'no such file(s).');
|
||||
if ($index2 === false) {
|
||||
$index3 = strpos($result, 'change');
|
||||
if (!($index3 === false)) {
|
||||
$phrase = trim(substr($result, $index3));
|
||||
$fields = explode(' ', $phrase);
|
||||
$id = $fields[1];
|
||||
$composerJson = substr($identifier, 0, $index) . '/composer.json@' . $id;
|
||||
|
||||
return $this->getComposerInformationFromPath($composerJson);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public function getBranches()
|
||||
{
|
||||
$possibleBranches = array();
|
||||
if (!$this->isStream()) {
|
||||
$possibleBranches[$this->p4Branch] = $this->getStream();
|
||||
} else {
|
||||
$command = $this->generateP4Command('streams //' . $this->p4Depot . '/...');
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
$resArray = explode(PHP_EOL, $result);
|
||||
foreach ($resArray as $line) {
|
||||
$resBits = explode(' ', $line);
|
||||
if (count($resBits) > 4) {
|
||||
$branch = preg_replace('/[^A-Za-z0-9 ]/', '', $resBits[4]);
|
||||
$possibleBranches[$branch] = $resBits[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
$branches = array();
|
||||
$branches['master'] = $possibleBranches[$this->p4Branch];
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
public function getTags()
|
||||
{
|
||||
$command = $this->generateP4Command('labels');
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
$resArray = explode(PHP_EOL, $result);
|
||||
$tags = array();
|
||||
foreach ($resArray as $line) {
|
||||
$index = strpos($line, 'Label');
|
||||
if (!($index === false)) {
|
||||
$fields = explode(' ', $line);
|
||||
$tags[$fields[1]] = $this->getStream() . '@' . $fields[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function checkStream()
|
||||
{
|
||||
$command = $this->generateP4Command('depots', false);
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
$resArray = explode(PHP_EOL, $result);
|
||||
foreach ($resArray as $line) {
|
||||
$index = strpos($line, 'Depot');
|
||||
if (!($index === false)) {
|
||||
$fields = explode(' ', $line);
|
||||
if (strcmp($this->p4Depot, $fields[1]) === 0) {
|
||||
$this->p4DepotType = $fields[3];
|
||||
|
||||
return $this->isStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getChangeList($reference)
|
||||
{
|
||||
$index = strpos($reference, '@');
|
||||
if ($index === false) {
|
||||
return;
|
||||
}
|
||||
$label = substr($reference, $index);
|
||||
$command = $this->generateP4Command(' changes -m1 ' . $label);
|
||||
$this->executeCommand($command);
|
||||
$changes = $this->commandResult;
|
||||
if (strpos($changes, 'Change') !== 0) {
|
||||
return;
|
||||
}
|
||||
$fields = explode(' ', $changes);
|
||||
$changeList = $fields[1];
|
||||
|
||||
return $changeList;
|
||||
}
|
||||
|
||||
public function getCommitLogs($fromReference, $toReference)
|
||||
{
|
||||
$fromChangeList = $this->getChangeList($fromReference);
|
||||
if ($fromChangeList == null) {
|
||||
return;
|
||||
}
|
||||
$toChangeList = $this->getChangeList($toReference);
|
||||
if ($toChangeList == null) {
|
||||
return;
|
||||
}
|
||||
$index = strpos($fromReference, '@');
|
||||
$main = substr($fromReference, 0, $index) . '/...';
|
||||
$command = $this->generateP4Command('filelog ' . $main . '@' . $fromChangeList. ',' . $toChangeList);
|
||||
$this->executeCommand($command);
|
||||
$result = $this->commandResult;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ use Composer\Downloader\TransportException;
|
|||
/**
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class RemoteFilesystem
|
||||
{
|
||||
|
@ -76,6 +77,16 @@ class RemoteFilesystem
|
|||
return $this->get($originUrl, $fileUrl, $options, null, $progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the options set in the constructor
|
||||
*
|
||||
* @return array Options
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file content or copy action.
|
||||
*
|
||||
|
|
|
@ -91,9 +91,9 @@ final class StreamContextFactory
|
|||
}
|
||||
|
||||
if (isset($proxy['user'])) {
|
||||
$auth = $proxy['user'];
|
||||
$auth = urldecode($proxy['user']);
|
||||
if (isset($proxy['pass'])) {
|
||||
$auth .= ':' . $proxy['pass'];
|
||||
$auth .= ':' . urldecode($proxy['pass']);
|
||||
}
|
||||
$auth = base64_encode($auth);
|
||||
|
||||
|
@ -120,7 +120,7 @@ final class StreamContextFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* A bug in PHP prevents the headers from correctly beeing sent when a content-type header is present and
|
||||
* A bug in PHP prevents the headers from correctly being sent when a content-type header is present and
|
||||
* NOT at the end of the array
|
||||
*
|
||||
* This method fixes the array by moving the content-type header to the end
|
||||
|
|
|
@ -92,6 +92,9 @@ class Svn
|
|||
if ($type !== 'out') {
|
||||
return;
|
||||
}
|
||||
if ('Redirecting to URL ' === substr($buffer, 0, 19)) {
|
||||
return;
|
||||
}
|
||||
$output .= $buffer;
|
||||
if ($verbose) {
|
||||
$io->write($buffer, false);
|
||||
|
@ -108,7 +111,9 @@ class Svn
|
|||
|
||||
// the error is not auth-related
|
||||
if (false === stripos($output, 'Could not authenticate to server:')
|
||||
&& false === stripos($output, 'svn: E170001:')) {
|
||||
&& false === stripos($output, 'authorization failed')
|
||||
&& false === stripos($output, 'svn: E170001:')
|
||||
&& false === stripos($output, 'svn: E215004:')) {
|
||||
throw new \RuntimeException($output);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue