1
0
Fork 0

Merge branch 'master' of https://github.com/composer/composer into add_exclude

# By Jordi Boggiano (30) and others
# Via Jordi Boggiano (37) and Morgan Campbell (1)
* 'master' of https://github.com/composer/composer: (83 commits)
  Update 01-basic-usage.md
  Revert 331425bcb3 as well, fixes #3612
  Revert "Disable overwrites when no-ansi is present, fixes #3612"
  Update deps
  Use justinrainbow/json-schema 1.4
  Improved wording
  Fix docs basic-auth => http-basic
  Add test for Generics class
  Single variable for traits and enums
  Use HHVM_VERSION instead of HPHP_VERSION
  Add support for using classmap to autoload Hack enums
  Re-use existing autoloader suffix if available, fixes #3701
  Report Travis CI build success early
  Test on HHVM nightly releases. Allow to fail.
  Make parseJson safer
  Use get home from Config instead of factory
  Fix env override regression, fixes #3820
  [create-project] Used no progress value for dependencies
  Add docBlock and fix CS
  Fix output of first line of progress when output is not decorated, refs #3818
  ...
pull/1607/head
msiebeneicher 2015-03-25 16:27:23 +01:00
commit 7522a33079
118 changed files with 1935 additions and 819 deletions

View File

@ -1,18 +1,34 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
addons:
apt_packages:
- parallel
php:
- 5.3.3
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
- hhvm-nightly
matrix:
fast_finish: true
allow_failures:
- php: hhvm-nightly
before_script:
- sudo apt-get install parallel
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
- composer install --prefer-source
- bin/composer install --prefer-source
- composer install
- bin/composer install
- git config --global user.name travis-ci
- git config --global user.email travis@example.com

View File

@ -32,9 +32,9 @@ if (function_exists('ini_set')) {
};
$memoryLimit = trim(ini_get('memory_limit'));
// Increase memory_limit if it is lower than 512M
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 512 * 1024 * 1024) {
@ini_set('memory_limit', '512M');
// Increase memory_limit if it is lower than 1GB
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1024) {
@ini_set('memory_limit', '1G');
}
unset($memoryInBytes, $memoryLimit);
}

View File

@ -23,14 +23,14 @@
},
"require": {
"php": ">=5.3.2",
"justinrainbow/json-schema": "~1.3",
"justinrainbow/json-schema": "~1.4",
"seld/jsonlint": "~1.0",
"symfony/console": "~2.3",
"symfony/console": "~2.5",
"symfony/finder": "~2.2",
"symfony/process": "~2.1"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "~4.5"
},
"suggest": {
"ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic",

316
composer.lock generated
View File

@ -4,20 +4,20 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "2bc9cc8aa706b68d611d7058e4eb8de7",
"hash": "8317ca3b690ea80633fe3fb2b0d440cb",
"packages": [
{
"name": "justinrainbow/json-schema",
"version": "1.3.7",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/justinrainbow/json-schema.git",
"reference": "87b54b460febed69726c781ab67462084e97a105"
"reference": "680d026082c3aa234b2d8617c50e9c73999913ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/87b54b460febed69726c781ab67462084e97a105",
"reference": "87b54b460febed69726c781ab67462084e97a105",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/680d026082c3aa234b2d8617c50e9c73999913ba",
"reference": "680d026082c3aa234b2d8617c50e9c73999913ba",
"shasum": ""
},
"require": {
@ -70,20 +70,20 @@
"json",
"schema"
],
"time": "2014-08-25 02:48:14"
"time": "2015-03-23 20:38:38"
},
{
"name": "seld/jsonlint",
"version": "1.3.0",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/jsonlint.git",
"reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35"
"reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/a7bc2ec9520ad15382292591b617c43bdb1fec35",
"reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/863ae85c6d3ef60ca49cb12bd051c4a0648c40c4",
"reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4",
"shasum": ""
},
"require": {
@ -116,21 +116,21 @@
"parser",
"validator"
],
"time": "2014-09-05 15:36:20"
"time": "2015-01-04 21:18:15"
},
{
"name": "symfony/console",
"version": "v2.6.1",
"version": "v2.6.5",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8"
"reference": "53f86497ccd01677e22435cfb7262599450a90d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/ef825fd9f809d275926547c9e57cbf14968793e8",
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8",
"url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1",
"reference": "53f86497ccd01677e22435cfb7262599450a90d1",
"shasum": ""
},
"require": {
@ -139,6 +139,7 @@
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.1"
},
"suggest": {
@ -173,26 +174,29 @@
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20"
"time": "2015-03-13 17:37:22"
},
{
"name": "symfony/finder",
"version": "v2.6.1",
"version": "v2.6.5",
"target-dir": "Symfony/Component/Finder",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
"reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721"
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721",
"reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721",
"url": "https://api.github.com/repos/symfony/Finder/zipball/bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -220,26 +224,29 @@
],
"description": "Symfony Finder Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20"
"time": "2015-03-12 10:28:44"
},
{
"name": "symfony/process",
"version": "v2.6.1",
"version": "v2.6.5",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a"
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/bf0c9bd625f13b0b0bbe39919225cf145dfb935a",
"reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a",
"url": "https://api.github.com/repos/symfony/Process/zipball/4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -267,7 +274,7 @@
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20"
"time": "2015-03-12 10:28:44"
}
],
"packages-dev": [
@ -326,17 +333,125 @@
"time": "2014-10-13 12:58:55"
},
{
"name": "phpunit/php-code-coverage",
"version": "2.0.14",
"name": "phpdocumentor/reflection-docblock",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94"
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca158276c1200cc27f5409a5e338486bc0b4fc94",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-0": {
"phpDocumentor": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"time": "2015-02-03 12:10:50"
},
{
"name": "phpspec/prophecy",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9",
"shasum": ""
},
"require": {
"doctrine/instantiator": "~1.0,>=1.0.2",
"phpdocumentor/reflection-docblock": "~2.0"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Prophecy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Marcello Duarte",
"email": "marcello.duarte@gmail.com"
}
],
"description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "http://phpspec.org",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"spy",
"stub"
],
"time": "2014-11-17 16:23:49"
},
{
"name": "phpunit/php-code-coverage",
"version": "2.0.15",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "34cc484af1ca149188d0d9e91412191e398e0b67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/34cc484af1ca149188d0d9e91412191e398e0b67",
"reference": "34cc484af1ca149188d0d9e91412191e398e0b67",
"shasum": ""
},
"require": {
@ -349,7 +464,7 @@
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4.1"
"phpunit/phpunit": "~4"
},
"suggest": {
"ext-dom": "*",
@ -368,9 +483,6 @@
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
@ -388,7 +500,7 @@
"testing",
"xunit"
],
"time": "2014-12-26 13:28:33"
"time": "2015-01-24 10:06:35"
},
{
"name": "phpunit/php-file-iterator",
@ -525,16 +637,16 @@
},
{
"name": "phpunit/php-token-stream",
"version": "1.3.0",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876"
"reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db32c18eba00b121c145575fcbcd4d4d24e6db74",
"reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74",
"shasum": ""
},
"require": {
@ -547,7 +659,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.4-dev"
}
},
"autoload": {
@ -570,20 +682,20 @@
"keywords": [
"tokenizer"
],
"time": "2014-08-31 06:12:13"
"time": "2015-01-17 09:51:32"
},
{
"name": "phpunit/phpunit",
"version": "4.4.1",
"version": "4.5.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a"
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a5e49a86ce5e33b8d0657abe145057fc513543a",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5b578d3865a9128b9c209b011fda6539ec06e7a5",
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5",
"shasum": ""
},
"require": {
@ -593,15 +705,16 @@
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpspec/prophecy": "~1.3.1",
"phpunit/php-code-coverage": "~2.0",
"phpunit/php-file-iterator": "~1.3.2",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "~1.0.2",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.0",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.1",
"sebastian/environment": "~1.1",
"sebastian/exporter": "~1.0",
"sebastian/environment": "~1.2",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0",
"symfony/yaml": "~2.0"
@ -615,7 +728,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.4.x-dev"
"dev-master": "4.5.x-dev"
}
},
"autoload": {
@ -641,7 +754,7 @@
"testing",
"xunit"
],
"time": "2014-12-28 07:57:05"
"time": "2015-02-05 15:51:19"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -700,25 +813,25 @@
},
{
"name": "sebastian/comparator",
"version": "1.1.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a"
"reference": "1dd8869519a225f7f2b9eb663e225298fade819e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e",
"reference": "1dd8869519a225f7f2b9eb663e225298fade819e",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.1",
"sebastian/exporter": "~1.0"
"sebastian/diff": "~1.2",
"sebastian/exporter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.1"
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
@ -760,7 +873,7 @@
"compare",
"equality"
],
"time": "2014-11-16 21:32:38"
"time": "2015-01-29 16:28:08"
},
{
"name": "sebastian/diff",
@ -866,28 +979,29 @@
},
{
"name": "sebastian/exporter",
"version": "1.0.2",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0"
"reference": "84839970d05254c73cde183a721c7af13aede943"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943",
"reference": "84839970d05254c73cde183a721c7af13aede943",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=5.3.3",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.2.x-dev"
}
},
"autoload": {
@ -927,7 +1041,7 @@
"export",
"exporter"
],
"time": "2014-09-10 00:51:36"
"time": "2015-01-27 07:23:06"
},
{
"name": "sebastian/global-state",
@ -980,6 +1094,59 @@
],
"time": "2014-10-06 09:23:50"
},
{
"name": "sebastian/recursion-context",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "3989662bbb30a29d20d9faa04a846af79b276252"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252",
"reference": "3989662bbb30a29d20d9faa04a846af79b276252",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2015-01-24 09:48:32"
},
{
"name": "sebastian/version",
"version": "1.0.4",
@ -1017,22 +1184,25 @@
},
{
"name": "symfony/yaml",
"version": "v2.6.1",
"version": "v2.6.5",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20"
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -1060,7 +1230,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20"
"time": "2015-03-12 10:28:44"
}
],
"aliases": [],

View File

@ -140,7 +140,7 @@ versions from `composer.json` and create the lock file after executing the `upd
command.
This means that if any of the dependencies get a new version, you won't get the updates
automatically. To update to the new version, use `update` command. This will fetch
automatically. To update to the new version, use the `update` command. This will fetch
the latest matching versions (according to your `composer.json` file) and also update
the lock file with the new version.

View File

@ -507,8 +507,13 @@ Lists the name, version and license of every package installed. Use
## run-script
### Options
* **--no-dev:** Disable dev mode
* **--list:** List user defined scripts
To run [scripts](articles/scripts.md) manually you can use this command,
just give it the script name and optionally --no-dev to disable the dev mode.
just give it the script name and optionally any required arguments.
## diagnose

View File

@ -894,4 +894,36 @@ The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`,
Optional.
&larr; [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) &rarr;
### non-feature-branches
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something), that will NOT be handled as feature branches. This is an array of string.
If you have non-numeric branch names, for example like "latest", "current", "latest-stable"
or something, that do not look like a version number, then composer handles such branches
as feature branches. This means it searches for parent branches, that look like a version
or ends at special branches (like master) and the root package version number becomes the
version of the parent branch or at least master or something.
To handle non-numeric named branches as versions instead of searching for a parent branch
with a valid version or special branch name like master, you can set patterns for branch
names, that should be handled as dev version branches.
This is really helpful when you have dependencies using "self.version", so that not dev-master,
but the same branch is installed (in the example: latest-testing).
An example:
If you have a testing branch, that is heavily maintained during a testing phase and is
deployed to your staging environment, normally "composer show -s" will give you `versions : * dev-master`.
If you configure latest-.* as a pattern for non-feature-branches like this:
{
"non-feature-branches": ["latest-.*"]
}
Then "composer show -s" will give you `versions : * dev-latest-testing`.
Optional.
&larr; [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) &rarr;

View File

@ -234,7 +234,7 @@ project to use the patched version. If the library is on GitHub (this is the
case most of the time), you can simply fork it there and push your changes to
your fork. After that you update the project's `composer.json`. All you have
to do is add your fork as a repository and update the version constraint to
point to your custom branch. For version constraint naming conventions see
point to your custom branch. Your custom branch name must be prefixed with `"dev-"`. For version constraint naming conventions see
[Libraries](02-libraries.md) for more information.
Example assuming you patched monolog to fix a bug in the `bugfix` branch:
@ -533,10 +533,9 @@ There are a few tools that can help you create a `composer` repository.
### Packagist
The underlying application used by packagist is open source. This means that you
can just install your own copy of packagist, re-brand, and use it. It's really
quite straight-forward to do. However due to its size and complexity, for most
small and medium sized companies willing to track a few packages will be better
off using Satis.
can technically install your own copy of packagist. However it is not a
supported use case and changes will happen without caring for third parties
using the code.
Packagist is a Symfony2 application, and it is [available on
GitHub](https://github.com/composer/packagist). It uses composer internally and
@ -544,8 +543,11 @@ acts as a proxy between VCS repositories and the composer users. It holds a list
of all VCS packages, periodically re-crawls them, and exposes them as a composer
repository.
To set your own copy, simply follow the instructions from the [packagist
github repository](https://github.com/composer/packagist).
### Toran Proxy
[Toran Proxy](https://toranproxy.com/) is a web app much like Packagist but
providing private package hosting as well as mirroring/proxying of GitHub and packagist.org. Check its homepage and the [Satis/Toran Proxy article](articles/handling-private-packages-with-satis.md)
for more information.
### Satis

View File

@ -6,9 +6,15 @@
# Toran Proxy
[Toran Proxy](https://toranproxy.com/) is a commercial alternative to Satis offering professional support as well as a web UI to manage everything and a better integration with Composer.
[Toran Proxy](https://toranproxy.com/) is a commercial alternative to Satis
offering professional support as well as a web UI to manage everything and a
better integration with Composer. It also provides proxying/mirroring for git
repos and package zip files which makes installs faster and independent from
third party systems.
Toran's revenue is also used to pay for Composer and Packagist development and hosting so using it is a good way to support open source financially. You can find more information about how to set it up and use it on the [Toran Proxy](https://toranproxy.com/) website.
Toran's revenue is also used to pay for Composer and Packagist development and
hosting so using it is a good way to support open source financially. You can
find more information about how to set it up and use it on the [Toran Proxy](https://toranproxy.com/) website.
# Satis
@ -151,6 +157,24 @@ Example using HTTP over SSL using a client certificate:
> **Tip:** See [ssl context options](http://www.php.net/manual/en/context.ssl.php) for more information.
### Authentification
When your private repositories are password protected, you can store the authentification details permanently.
The first time Composer needs to authenticate against some domain it will prompt you for a username/password
and then you will be asked whether you want to store it.
The storage can be done either globally in the `COMPOSER_HOME/auth.json` file (`COMPOSER_HOME` defaults to
`~/.composer` or `%APPDATA%/Composer` on Windows) or also in the project directory directly sitting besides your
composer.json.
You can also configure these by hand using the config command if you need to configure a production machine
to be able to run non-interactive installs. For example to enter credentials for example.org one could type:
composer config http-basic.example.org username password
That will store it in the current directory's auth.json, but if you want it available globally you can use the
`--global` (`-g`) flag.
### Downloads
When GitHub or BitBucket repositories are mirrored on your local satis, the build process will include

View File

@ -40,7 +40,7 @@ username/password pairs, for example:
```json
{
"basic-auth": {
"http-basic": {
"repo.example1.org": {
"username": "my-username1",
"password": "my-secret-password1"

View File

@ -82,15 +82,7 @@ Furthermore plugins may implement the
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].
Plugin can subscribe to any of the available [script events](scripts.md#event-names).
Example:
@ -148,7 +140,7 @@ list of installed packages. Additionally all plugin packages installed in the
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
> installed plugins. 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
@ -157,4 +149,3 @@ local project plugins are loaded.
[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

View File

@ -20,20 +20,16 @@ the Composer execution process.
Composer fires the following named events during its execution process:
### Command Events
- **pre-install-cmd**: occurs before the `install` command is executed.
- **post-install-cmd**: occurs after the `install` command is executed.
- **pre-update-cmd**: occurs before the `update` command is executed.
- **post-update-cmd**: occurs after the `update` command is executed.
- **pre-status-cmd**: occurs before the `status` command is executed.
- **post-status-cmd**: occurs after the `status` command is executed.
- **pre-dependencies-solving**: occurs before the dependencies are resolved.
- **post-dependencies-solving**: occurs after the dependencies are resolved.
- **pre-package-install**: occurs before a package is installed.
- **post-package-install**: occurs after a package is installed.
- **pre-package-update**: occurs before a package is updated.
- **post-package-update**: occurs after a package is updated.
- **pre-package-uninstall**: occurs before a package has been uninstalled.
- **post-package-uninstall**: occurs after a package has been uninstalled.
- **pre-archive-cmd**: occurs before the `archive` command is executed.
- **post-archive-cmd**: occurs after the `archive` command is executed.
- **pre-autoload-dump**: occurs before the autoloader is dumped, either
during `install`/`update`, or via the `dump-autoload` command.
- **post-autoload-dump**: occurs after the autoloader is dumped, either
@ -42,8 +38,28 @@ Composer fires the following named events during its execution process:
installed, during the `create-project` command.
- **post-create-project-cmd**: occurs after the `create-project` command is
executed.
- **pre-archive-cmd**: occurs before the `archive` command is executed.
- **post-archive-cmd**: occurs after the `archive` command is executed.
### Installer Events
- **pre-dependencies-solving**: occurs before the dependencies are resolved.
- **post-dependencies-solving**: occurs after the dependencies are resolved.
### Package Events
- **pre-package-install**: occurs before a package is installed.
- **post-package-install**: occurs after a package is installed.
- **pre-package-update**: occurs before a package is updated.
- **post-package-update**: occurs after a package is updated.
- **pre-package-uninstall**: occurs before a package has been uninstalled.
- **post-package-uninstall**: occurs after a package has been uninstalled.
### Plugin Events
- **command**: occurs before any Composer Command is executed on the CLI. It
provides you with access to the input and output objects of the program.
- **pre-file-download**: occurs before files are downloaded and allows
you to manipulate the `RemoteFilesystem` object prior to downloading files
based on the URL to be downloaded.
> **Note:** Composer makes no assumptions about the state of your dependencies
> prior to `install` or `update`. Therefore, you should not specify scripts
@ -96,6 +112,7 @@ that might be used to execute the PHP callbacks:
namespace MyVendor;
use Composer\Script\Event;
use Composer\Installer\PackageEvent;
class MyClass
{
@ -105,7 +122,7 @@ class MyClass
// do stuff
}
public static function postPackageInstall(Event $event)
public static function postPackageInstall(PackageEvent $event)
{
$installedPackage = $event->getOperation()->getPackage();
// do stuff
@ -118,14 +135,21 @@ class MyClass
}
```
When an event is fired, Composer's internal event handler receives a
`Composer\Script\Event` object, which is passed as the first argument to your
PHP callback. This `Event` object has getters for other contextual objects:
When an event is fired, your PHP callback receives as first argument an
`Composer\EventDispatcher\Event` object. This object has a `getName()` method
that lets you retrieve event name.
- `getComposer()`: returns the current instance of `Composer\Composer`
- `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
Depending on the script types (see list above) you will get various event
subclasses containing various getters with relevant data and associated
objects:
- Base class: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html)
- Command Events: [`Composer\Script\Event`](https://getcomposer.org/apidoc/master/Composer/Script/Event.html)
- Installer Events: [`Composer\Installer\InstallerEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/InstallerEvent.html)
- Package Events: [`Composer\Installer\PackageEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/PackageEvent.html)
- Plugin Events:
- command: [`Composer\Plugin\CommandEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/CommandEvent.html)
- pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/PreFileDownloadEvent.html)
## Running scripts manually

View File

@ -14,7 +14,7 @@ compatible with the new major version of your dependency.
For example instead of using `>=3.4` you should use `~3.4` which allows all
versions up to `3.999` but does not include `4.0` and above. The `~` operator
works very well with libraries follow [semantic versioning](http://semver.org).
works very well with libraries following [semantic versioning](http://semver.org).
**Note:** As a package maintainer, you can make the life of your users easier
by providing an [alias version](../articles/aliases.md) for your development

View File

@ -413,6 +413,13 @@
"format": "uri"
}
}
},
"non-feature-branches": {
"type": ["array"],
"description": "A set of string or regex patterns for non-numeric branch names that will not be handles as feature branches.",
"items": {
"type": "string"
}
}
}
}

View File

@ -216,7 +216,16 @@ EOF;
$classmapFile .= ");\n";
if (!$suffix) {
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
if (is_readable($vendorPath.'/autoload.php')) {
$content = file_get_contents($vendorPath.'/autoload.php');
if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) {
$suffix = $match[1];
}
}
if (!$suffix) {
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
}
}
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
@ -393,7 +402,7 @@ EOF;
}
if (!$filesCode) {
return FALSE;
return false;
}
return <<<EOF

View File

@ -92,7 +92,7 @@ class ClassMapGenerator
if (!isset($map[$class])) {
$map[$class] = $filePath;
} elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
$io->write(
$io->writeError(
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'
);
@ -112,7 +112,10 @@ class ClassMapGenerator
*/
private static function findClasses($path)
{
$traits = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait';
$extraTypes = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait';
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) {
$extraTypes .= '|enum';
}
try {
$contents = @php_strip_whitespace($path);
@ -129,7 +132,7 @@ class ClassMapGenerator
}
// return early if there is no chance of matching anything in this file
if (!preg_match('{\b(?:class|interface'.$traits.')\s}i', $contents)) {
if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) {
return array();
}
@ -154,7 +157,7 @@ class ClassMapGenerator
preg_match_all('{
(?:
\b(?<![\$:>])(?P<type>class|interface'.$traits.') \s+ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*)
\b(?<![\$:>])(?P<type>class|interface'.$extraTypes.') \s+ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*)
| \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;]
)
}ix', $contents, $matches);
@ -170,6 +173,12 @@ class ClassMapGenerator
if ($name[0] === ':') {
// This is an XHP class, https://github.com/facebook/xhp
$name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
} else if ($matches['type'][$i] === 'enum') {
// In Hack, something like:
// enum Foo: int { HERP = '123'; }
// The regex above captures the colon, which isn't part of
// the class name.
$name = rtrim($name, ':');
}
$classes[] = ltrim($namespace . $name, '\\');
}

View File

@ -65,7 +65,7 @@ class Cache
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if ($this->enabled && file_exists($this->root . $file)) {
if ($this->io->isDebug()) {
$this->io->write('Reading '.$this->root . $file.' from cache');
$this->io->writeError('Reading '.$this->root . $file.' from cache');
}
return file_get_contents($this->root . $file);
@ -80,7 +80,7 @@ class Cache
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if ($this->io->isDebug()) {
$this->io->write('Writing '.$this->root . $file.' into cache');
$this->io->writeError('Writing '.$this->root . $file.' into cache');
}
try {
@ -98,7 +98,7 @@ class Cache
@disk_free_space($this->root . dirname($file))
);
$this->io->write($message);
$this->io->writeError($message);
return false;
}
@ -120,7 +120,7 @@ class Cache
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
if ($this->io->isDebug()) {
$this->io->write('Writing '.$this->root . $file.' into cache');
$this->io->writeError('Writing '.$this->root . $file.' into cache');
}
return copy($source, $this->root . $file);
@ -139,7 +139,7 @@ class Cache
touch($this->root . $file);
if ($this->io->isDebug()) {
$this->io->write('Reading '.$this->root . $file.' from cache');
$this->io->writeError('Reading '.$this->root . $file.' from cache');
}
return copy($this->root . $file, $target);

View File

@ -34,7 +34,7 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln(<<<EOT
$this->getIO()->write(<<<EOT
<info>Composer - Package Management for PHP</info>
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
See http://getcomposer.org/ for more information.</comment>

View File

@ -97,7 +97,7 @@ EOT
$package = $this->getComposer()->getPackage();
}
$io->write('<info>Creating the archive.</info>');
$io->writeError('<info>Creating the archive.</info>');
$archiveManager->archive($package, $format, $dest);
return 0;
@ -105,14 +105,14 @@ EOT
protected function selectPackage(IOInterface $io, $packageName, $version = null)
{
$io->write('<info>Searching for the specified package.</info>');
$io->writeError('<info>Searching for the specified package.</info>');
if ($composer = $this->getComposer(false)) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
$io->write('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
$io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
$repos = new CompositeRepository($defaultRepos);
}
@ -125,14 +125,14 @@ EOT
if (count($packages) > 1) {
$package = reset($packages);
$io->write('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
$io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
$io->write('<comment>Please use a more specific constraint to pick a different package.</comment>');
$io->writeError('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
$io->writeError('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
$io->writeError('<comment>Please use a more specific constraint to pick a different package.</comment>');
} elseif ($packages) {
$package = reset($packages);
$io->write('<info>Found an exact match '.$package->getPrettyString().'.</info>');
$io->writeError('<info>Found an exact match '.$package->getPrettyString().'.</info>');
} else {
$io->write('<error>Could not find a package matching '.$packageName.'.</error>');
$io->writeError('<error>Could not find a package matching '.$packageName.'.</error>');
return false;
}

View File

@ -51,21 +51,21 @@ EOT
foreach ($cachePaths as $key => $cachePath) {
$cachePath = realpath($cachePath);
if (!$cachePath) {
$io->write("<info>Cache directory does not exist ($key): $cachePath</info>");
$io->writeError("<info>Cache directory does not exist ($key): $cachePath</info>");
return;
continue;
}
$cache = new Cache($io, $cachePath);
if (!$cache->isEnabled()) {
$io->write("<info>Cache is not enabled ($key): $cachePath</info>");
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
return;
continue;
}
$io->write("<info>Clearing cache ($key): $cachePath</info>");
$io->writeError("<info>Clearing cache ($key): $cachePath</info>");
$cache->gc(0, 0);
}
$io->write('<info>All caches cleared.</info>');
$io->writeError('<info>All caches cleared.</info>');
}
}

View File

@ -33,15 +33,25 @@ class ConfigCommand extends Command
protected $config;
/**
* @var Composer\Json\JsonFile
* @var JsonFile
*/
protected $configFile;
/**
* @var Composer\Config\JsonConfigSource
* @var JsonConfigSource
*/
protected $configSource;
/**
* @var JsonFile
*/
protected $authConfigFile;
/**
* @var JsonConfigSource
*/
protected $authConfigSource;
/**
* {@inheritDoc}
*/
@ -65,6 +75,15 @@ class ConfigCommand extends Command
This command allows you to edit some basic composer settings in either the
local composer.json file or the global config.json file.
To set a config setting:
<comment>%command.full_name% bin-dir bin/</comment>
To read a config setting:
<comment>%command.full_name% bin-dir</comment>
Outputs: <info>bin</info>
To edit the global config.json file:
<comment>%command.full_name% --global</comment>
@ -73,7 +92,15 @@ To add a repository:
<comment>%command.full_name% repositories.foo vcs http://bar.com</comment>
You can add a repository to the global config.json file by passing in the
To remove a repository (repo is a short alias for repositories):
<comment>%command.full_name% --unset repo.foo</comment>
To disable packagist:
<comment>%command.full_name% repo.packagist false</comment>
You can alter repositories in the global config.json file by passing in the
<info>--global</info> option.
To edit the file in an external editor:
@ -230,55 +257,13 @@ EOT
$value = json_encode($value);
}
$output->writeln($value);
$this->getIO()->write($value);
return 0;
}
$values = $input->getArgument('setting-value'); // what the user is trying to add/change
// handle repositories
if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
return $this->configSource->removeRepository($matches[1]);
}
if (2 !== count($values)) {
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
}
return $this->configSource->addRepository($matches[1], array(
'type' => $values[0],
'url' => $values[1],
));
}
// handle github-oauth
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
return;
}
if ($matches[1] === 'github-oauth') {
if (1 !== count($values)) {
throw new \RuntimeException('Too many arguments, expected only one token');
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
} elseif ($matches[1] === 'http-basic') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
}
return;
}
$booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); };
$booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; };
@ -402,6 +387,55 @@ EOT
}
}
// handle repositories
if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
return $this->configSource->removeRepository($matches[1]);
}
if (2 === count($values)) {
return $this->configSource->addRepository($matches[1], array(
'type' => $values[0],
'url' => $values[1],
));
}
if (1 === count($values)) {
$bool = strtolower($values[0]);
if (true === $booleanValidator($bool) && false === $booleanNormalizer($bool)) {
return $this->configSource->addRepository($matches[1], false);
}
}
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
}
// handle github-oauth
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
return;
}
if ($matches[1] === 'github-oauth') {
if (1 !== count($values)) {
throw new \RuntimeException('Too many arguments, expected only one token');
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
} elseif ($matches[1] === 'http-basic') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
}
return;
}
throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
}
@ -450,9 +484,9 @@ EOT
}
if (is_string($rawVal) && $rawVal != $value) {
$output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
$this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
} else {
$output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
$this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
}
}
}

View File

@ -106,7 +106,7 @@ EOT
$this->updatePreferredOptions($config, $input, $preferSource, $preferDist);
if ($input->getOption('no-custom-installers')) {
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
@ -145,11 +145,13 @@ EOT
}
$composer = Factory::create($io, null, $disablePlugins);
$composer->getDownloadManager()->setOutputProgress(!$noProgress);
$fs = new Filesystem();
if ($noScripts === false) {
// dispatch event
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
}
$rootPackageConfig = $composer->getConfig();
@ -196,7 +198,7 @@ EOT
}
}
} catch (\Exception $e) {
$io->write('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
$io->writeError('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
}
$hasVcs = false;
@ -217,7 +219,7 @@ EOT
if ($noScripts === false) {
// dispatch event
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
}
chdir($oldCwd);
@ -288,10 +290,10 @@ EOT
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
}
$io->write('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
$io->writeError('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
if ($disablePlugins) {
$io->write('<info>Plugins have been disabled.</info>');
$io->writeError('<info>Plugins have been disabled.</info>');
}
if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
@ -311,10 +313,11 @@ EOT
$installedFromVcs = 'source' === $package->getInstallationSource();
$io->write('<info>Created project in ' . $directory . '</info>');
$io->writeError('<info>Created project in ' . $directory . '</info>');
chdir($directory);
putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion());
$_SERVER['COMPOSER_ROOT_VERSION'] = $package->getPrettyVersion();
putenv('COMPOSER_ROOT_VERSION='.$_SERVER['COMPOSER_ROOT_VERSION']);
return $installedFromVcs;
}

View File

@ -96,9 +96,9 @@ EOT
if ($messages) {
sort($messages);
$output->writeln($messages);
$this->getIO()->write($messages);
} else {
$output->writeln('<info>There is no installed package depending on "'.$needle.'".</info>');
$this->getIO()->writeError('<info>There is no installed package depending on "'.$needle.'".</info>');
}
}
}

View File

@ -59,8 +59,8 @@ EOT
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$output->write('Checking composer.json: ');
$this->outputResult($output, $this->checkComposerSchema());
$this->getIO()->write('Checking composer.json: ', false);
$this->outputResult($this->checkComposerSchema());
}
if ($composer) {
@ -72,37 +72,37 @@ EOT
$this->rfs = new RemoteFilesystem($this->getIO(), $config);
$this->process = new ProcessExecutor($this->getIO());
$output->write('Checking platform settings: ');
$this->outputResult($output, $this->checkPlatform());
$this->getIO()->write('Checking platform settings: ', false);
$this->outputResult($this->checkPlatform());
$output->write('Checking git settings: ');
$this->outputResult($output, $this->checkGit());
$this->getIO()->write('Checking git settings: ', false);
$this->outputResult($this->checkGit());
$output->write('Checking http connectivity: ');
$this->outputResult($output, $this->checkHttp());
$this->getIO()->write('Checking http connectivity: ', false);
$this->outputResult($this->checkHttp());
$opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
if (!empty($opts['http']['proxy'])) {
$output->write('Checking HTTP proxy: ');
$this->outputResult($output, $this->checkHttpProxy());
$output->write('Checking HTTP proxy support for request_fulluri: ');
$this->outputResult($output, $this->checkHttpProxyFullUriRequestParam());
$output->write('Checking HTTPS proxy support for request_fulluri: ');
$this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam());
$this->getIO()->write('Checking HTTP proxy: ', false);
$this->outputResult($this->checkHttpProxy());
$this->getIO()->write('Checking HTTP proxy support for request_fulluri: ', false);
$this->outputResult($this->checkHttpProxyFullUriRequestParam());
$this->getIO()->write('Checking HTTPS proxy support for request_fulluri: ', false);
$this->outputResult($this->checkHttpsProxyFullUriRequestParam());
}
if ($oauth = $config->get('github-oauth')) {
foreach ($oauth as $domain => $token) {
$output->write('Checking '.$domain.' oauth access: ');
$this->outputResult($output, $this->checkGithubOauth($domain, $token));
$this->getIO()->write('Checking '.$domain.' oauth access: ', false);
$this->outputResult($this->checkGithubOauth($domain, $token));
}
} else {
$output->write('Checking github.com rate limit: ');
$this->getIO()->write('Checking github.com rate limit: ', false);
$rate = $this->getGithubRateLimit('github.com');
if (10 > $rate['remaining']) {
$output->writeln('<warning>WARNING</warning>');
$output->writeln(sprintf(
$this->getIO()->write('<warning>WARNING</warning>');
$this->getIO()->write(sprintf(
'<comment>Github has a rate limit on their API. '
. 'You currently have <options=bold>%u</options=bold> '
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
@ -112,15 +112,15 @@ EOT
$rate['limit']
));
} else {
$output->writeln('<info>OK</info>');
$this->getIO()->write('<info>OK</info>');
}
}
$output->write('Checking disk free space: ');
$this->outputResult($output, $this->checkDiskSpace($config));
$this->getIO()->write('Checking disk free space: ', false);
$this->outputResult($this->checkDiskSpace($config));
$output->write('Checking composer version: ');
$this->outputResult($output, $this->checkVersion());
$this->getIO()->write('Checking composer version: ', false);
$this->outputResult($this->checkVersion());
return $this->failures;
}
@ -308,17 +308,17 @@ EOT
return true;
}
private function outputResult(OutputInterface $output, $result)
private function outputResult($result)
{
if (true === $result) {
$output->writeln('<info>OK</info>');
$this->getIO()->write('<info>OK</info>');
} else {
$this->failures++;
$output->writeln('<error>FAIL</error>');
$this->getIO()->write('<error>FAIL</error>');
if ($result instanceof \Exception) {
$output->writeln('['.get_class($result).'] '.$result->getMessage());
$this->getIO()->write('['.get_class($result).'] '.$result->getMessage());
} elseif ($result) {
$output->writeln(trim($result));
$this->getIO()->write(trim($result));
}
}
}
@ -343,10 +343,34 @@ EOT
}
$iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
if (!function_exists('json_decode')) {
$errors['json'] = true;
}
if (!extension_loaded('Phar')) {
$errors['phar'] = true;
}
if (!extension_loaded('filter')) {
$errors['filter'] = true;
}
if (!extension_loaded('hash')) {
$errors['hash'] = true;
}
if (!extension_loaded('ctype')) {
$errors['ctype'] = true;
}
if (!ini_get('allow_url_fopen')) {
$errors['allow_url_fopen'] = true;
}
if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
$errors['ioncube'] = ioncube_loader_version();
}
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$errors['php'] = PHP_VERSION;
}
@ -356,19 +380,13 @@ EOT
}
if (!extension_loaded('openssl')) {
$warnings['openssl'] = true;
$errors['openssl'] = true;
}
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
$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();
@ -384,19 +402,76 @@ EOT
}
}
if (ini_get('xdebug.profiler_enabled')) {
$warnings['xdebug_profile'] = true;
} elseif (extension_loaded('xdebug')) {
$warnings['xdebug_loaded'] = true;
}
if (!empty($errors)) {
foreach ($errors as $error => $current) {
switch ($error) {
case 'json':
$text = PHP_EOL."The json extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-json";
break;
case 'phar':
$text = PHP_EOL."The phar extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-phar";
break;
case 'filter':
$text = PHP_EOL."The filter extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-filter";
break;
case 'hash':
$text = PHP_EOL."The hash extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-hash";
break;
case 'ctype':
$text = PHP_EOL."The ctype extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-ctype";
break;
case 'unicode':
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " detect_unicode = Off";
$displayIniMessage = true;
break;
case 'suhosin':
$text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL;
$text .= " suhosin.executor.include.whitelist = phar ".$current;
$displayIniMessage = true;
break;
case 'php':
$text = "Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
break;
case 'allow_url_fopen':
$text = "The allow_url_fopen setting is incorrect.".PHP_EOL;
$text = PHP_EOL."The allow_url_fopen setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " allow_url_fopen = On";
$text .= " allow_url_fopen = On";
$displayIniMessage = true;
break;
case 'ioncube':
$text = PHP_EOL."Your ionCube Loader extension ($current) is incompatible with Phar files.".PHP_EOL;
$text .= "Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:".PHP_EOL;
$text .= " zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so";
$displayIniMessage = true;
break;
case 'openssl':
$text = PHP_EOL."The openssl extension is missing, which means that secure HTTPS transfers are impossible.".PHP_EOL;
$text .= "If possible you should enable it or recompile php with --with-openssl";
break;
}
$out($text, 'error');
}
@ -425,11 +500,6 @@ EOT
$text .= " Recompile it without this flag if possible";
break;
case 'openssl':
$text = "The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL;
$text .= " If possible you should enable it or recompile php with --with-openssl";
break;
case 'php':
$text = "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.";

View File

@ -55,9 +55,9 @@ EOT
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
if ($optimize) {
$output->writeln('<info>Generating optimized autoload files</info>');
$this->getIO()->writeError('<info>Generating optimized autoload files</info>');
} else {
$output->writeln('<info>Generating autoload files</info>');
$this->getIO()->writeError('<info>Generating autoload files</info>');
}
$generator = $composer->getAutoloadGenerator();

View File

@ -72,7 +72,7 @@ EOT
// change to global dir
$config = Factory::createConfig();
chdir($config->get('home'));
$output->writeln('<info>Changed current directory to '.$config->get('home').'</info>');
$this->getIO()->writeError('<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));

View File

@ -57,15 +57,21 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$repo = $this->initializeRepo();
$repos = $this->initializeRepos();
$return = 0;
foreach ($input->getArgument('packages') as $packageName) {
foreach ($repos as $repo) {
$package = $this->getPackage($repo, $packageName);
if ($package instanceof CompletePackageInterface) {
break;
}
}
$package = $this->getPackage($repo, $packageName);
if (!$package instanceof CompletePackageInterface) {
$return = 1;
$output->writeln('<warning>Package '.$packageName.' not found</warning>');
$this->getIO()->writeError('<warning>Package '.$packageName.' not found</warning>');
continue;
}
@ -78,13 +84,13 @@ EOT
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$return = 1;
$output->writeln('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
$this->getIO()->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
continue;
}
if ($input->getOption('show')) {
$output->writeln(sprintf('<info>%s</info>', $url));
$this->getIO()->write(sprintf('<info>%s</info>', $url));
} else {
$this->openBrowser($url);
}
@ -139,26 +145,30 @@ EOT
} elseif (0 === $osx) {
passthru('open ' . $url);
} else {
$this->getIO()->write('no suitable browser opening command found, open yourself: ' . $url);
$this->getIO()->writeError('no suitable browser opening command found, open yourself: ' . $url);
}
}
/**
* Initializes the repo
* Initializes repositories
*
* @return CompositeRepository
* Returns an array of repos in order they should be checked in
*
* @return RepositoryInterface[]
*/
private function initializeRepo()
private function initializeRepos()
{
$composer = $this->getComposer(false);
if ($composer) {
$repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
} else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
$repo = new CompositeRepository($defaultRepos);
return array(
$composer->getRepositoryManager()->getLocalRepository(),
new CompositeRepository($composer->getRepositoryManager()->getRepositories())
);
}
return $repo;
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
return array(new CompositeRepository($defaultRepos));
}
}

View File

@ -117,13 +117,13 @@ EOT
$json = $file->encode($options);
if ($input->isInteractive()) {
$output->writeln(array(
$this->getIO()->writeError(array(
'',
$json,
''
));
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
$this->getIO()->writeError('<error>Command aborted</error>');
return 1;
}
@ -154,14 +154,14 @@ EOT
$dialog = $this->getHelperSet()->get('dialog');
$formatter = $this->getHelperSet()->get('formatter');
$output->writeln(array(
$this->getIO()->writeError(array(
'',
$formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
''
));
// namespace
$output->writeln(array(
$this->getIO()->writeError(array(
'',
'This command will guide you through creating your composer.json config.',
'',
@ -266,7 +266,7 @@ EOT
);
$input->setOption('license', $license);
$output->writeln(array(
$this->getIO()->writeError(array(
'',
'Define your dependencies.',
''
@ -316,7 +316,7 @@ EOT
$version = $this->findBestVersionForPackage($input, $requirement['name']);
$requirement['version'] = $version;
$output->writeln(sprintf(
$this->getIO()->writeError(sprintf(
'Using version <info>%s</info> for <info>%s</info>',
$requirement['version'],
$requirement['name']
@ -329,6 +329,7 @@ EOT
return $result;
}
$versionParser = new VersionParser();
while (null !== $package = $dialog->ask($output, $prompt)) {
$matches = $this->findPackages($package);
@ -345,31 +346,41 @@ EOT
// no match, prompt which to pick
if (!$exactMatch) {
$output->writeln(array(
$this->getIO()->writeError(array(
'',
sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
''
));
$output->writeln($choices);
$output->writeln('');
$this->getIO()->writeError($choices);
$this->getIO()->writeError('');
$validator = function ($selection) use ($matches) {
$validator = function ($selection) use ($matches, $versionParser) {
if ('' === $selection) {
return false;
}
if (!is_numeric($selection) && preg_match('{^\s*(\S+)\s+(\S.*)\s*$}', $selection, $matches)) {
return $matches[1].' '.$matches[2];
if (is_numeric($selection) && isset($matches[(int) $selection])) {
$package = $matches[(int) $selection];
return $package['name'];
}
if (!isset($matches[(int) $selection])) {
throw new \Exception('Not a valid selection');
if (preg_match('{^\s*(?P<name>[\S/]+)(?:\s+(?P<version>\S+))?\s*$}', $selection, $packageMatches)) {
if (isset($packageMatches['version'])) {
// parsing `acme/example ~2.3`
// validate version constraint
$versionParser->parseConstraints($packageMatches['version']);
return $packageMatches['name'].' '.$packageMatches['version'];
}
// parsing `acme/example`
return $packageMatches['name'];
}
$package = $matches[(int) $selection];
return $package['name'];
throw new \Exception('Not a valid selection');
};
$package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add, or the complete package name if it is not listed', false, ':'), $validator, 3);
@ -392,7 +403,7 @@ EOT
if (false === $constraint) {
$constraint = $this->findBestVersionForPackage($input, $package);
$output->writeln(sprintf(
$this->getIO()->writeError(sprintf(
'Using version <info>%s</info> for <info>%s</info>',
$constraint,
$package

View File

@ -65,18 +65,18 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($args = $input->getArgument('packages')) {
$output->writeln('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
$this->getIO()->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
return 1;
}
if ($input->getOption('no-custom-installers')) {
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$output->writeln('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
$this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));

View File

@ -18,7 +18,8 @@ use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@ -68,14 +69,17 @@ EOT
switch ($format = $input->getOption('format')) {
case 'text':
$output->writeln('Name: <comment>'.$root->getPrettyName().'</comment>');
$output->writeln('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
$output->writeln('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
$output->writeln('Dependencies:');
$this->getIO()->write('Name: <comment>'.$root->getPrettyName().'</comment>');
$this->getIO()->write('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
$this->getIO()->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
$this->getIO()->write('Dependencies:');
$this->getIO()->write('');
$table = $this->getHelperSet()->get('table');
$table->setLayout(TableHelper::LAYOUT_BORDERLESS);
$table->setHorizontalBorderChar('');
$table = new Table($output);
$table->setStyle('compact');
$table->getStyle()->setVerticalBorderChar('');
$table->getStyle()->setCellRowContentFormat('%s ');
$table->setHeaders(array('Name', 'Version', 'License'));
foreach ($packages as $package) {
$table->addRow(array(
$package->getPrettyName(),
@ -83,7 +87,7 @@ EOT
implode(', ', $package->getLicense()) ?: 'none',
));
}
$table->render($output);
$table->render();
break;
case 'json':
@ -94,7 +98,7 @@ EOT
);
}
$output->writeln(JsonFile::encode(array(
$this->getIO()->write(JsonFile::encode(array(
'name' => $root->getPrettyName(),
'version' => $versionParser->formatVersion($root),
'license' => $root->getLicense(),

View File

@ -73,7 +73,7 @@ EOT
if (isset($composer[$type][$package])) {
$json->removeLink($type, $package);
} elseif (isset($composer[$altType][$package])) {
$output->writeln('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
$this->getIO()->writeError('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
$dialog = $this->getHelperSet()->get('dialog');
if ($this->getIO()->isInteractive()) {
if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) {
@ -81,7 +81,7 @@ EOT
}
}
} else {
$output->writeln('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
$this->getIO()->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
}
}
@ -111,7 +111,7 @@ EOT
$status = $install->run();
if ($status !== 0) {
$output->writeln("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
$this->getIO()->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
file_put_contents($jsonFile->getPath(), $composerBackup);
}

View File

@ -67,17 +67,17 @@ EOT
$newlyCreated = !file_exists($file);
if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
$output->writeln('<error>'.$file.' could not be created.</error>');
$this->getIO()->writeError('<error>'.$file.' could not be created.</error>');
return 1;
}
if (!is_readable($file)) {
$output->writeln('<error>'.$file.' is not readable.</error>');
$this->getIO()->writeError('<error>'.$file.' is not readable.</error>');
return 1;
}
if (!is_writable($file)) {
$output->writeln('<error>'.$file.' is not writable.</error>');
$this->getIO()->writeError('<error>'.$file.' is not writable.</error>');
return 1;
}
@ -122,7 +122,7 @@ EOT
$json->write($composerDefinition);
}
$output->writeln('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
$this->getIO()->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
if ($input->getOption('no-update')) {
return 0;
@ -154,10 +154,10 @@ EOT
$status = $install->run();
if ($status !== 0) {
if ($newlyCreated) {
$output->writeln("\n".'<error>Installation failed, deleting '.$file.'.</error>');
$this->getIO()->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
unlink($json->getPath());
} else {
$output->writeln("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
$this->getIO()->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
file_put_contents($json->getPath(), $composerBackup);
}
}

View File

@ -27,7 +27,7 @@ class RunScriptCommand extends Command
/**
* @var array Array with command events
*/
protected $commandEvents = array(
protected $scriptEvents = array(
ScriptEvents::PRE_INSTALL_CMD,
ScriptEvents::POST_INSTALL_CMD,
ScriptEvents::PRE_UPDATE_CMD,
@ -35,17 +35,11 @@ class RunScriptCommand extends Command
ScriptEvents::PRE_STATUS_CMD,
ScriptEvents::POST_STATUS_CMD,
ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
ScriptEvents::POST_CREATE_PROJECT_CMD
);
/**
* @var array Array with script events
*/
protected $scriptEvents = array(
ScriptEvents::POST_CREATE_PROJECT_CMD,
ScriptEvents::PRE_ARCHIVE_CMD,
ScriptEvents::POST_ARCHIVE_CMD,
ScriptEvents::PRE_AUTOLOAD_DUMP,
ScriptEvents::POST_AUTOLOAD_DUMP
ScriptEvents::POST_AUTOLOAD_DUMP,
);
protected function configure()
@ -54,10 +48,11 @@ class RunScriptCommand extends Command
->setName('run-script')
->setDescription('Run the scripts defined in composer.json.')
->setDefinition(array(
new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'),
new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.'),
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List scripts.'),
))
->setHelp(<<<EOT
The <info>run-script</info> command runs scripts defined in composer.json:
@ -70,8 +65,14 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('list')) {
return $this->listScripts();
} elseif (!$input->getArgument('script')) {
throw new \RunTimeException('Missing required argument "script"');
}
$script = $input->getArgument('script');
if (!in_array($script, $this->commandEvents) && !in_array($script, $this->scriptEvents)) {
if (!in_array($script, $this->scriptEvents)) {
if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script));
}
@ -86,15 +87,28 @@ EOT
// add the bin dir to the PATH to make local binaries of deps usable in scripts
$binDir = $composer->getConfig()->get('bin-dir');
if (is_dir($binDir)) {
putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
$_SERVER['PATH'] = realpath($binDir).PATH_SEPARATOR.getenv('PATH');
putenv('PATH='.$_SERVER['PATH']);
}
$args = $input->getArgument('args');
if (in_array($script, $this->commandEvents)) {
return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
}
return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
}
protected function listScripts()
{
$scripts = $this->getComposer()->getPackage()->getScripts();
if (!count($scripts)) {
return 0;
}
$this->getIO()->writeError('<info>scripts:</info>');
foreach ($scripts as $name => $script) {
$this->getIO()->write(' ' . $name);
}
return 0;
}
}

View File

@ -57,7 +57,8 @@ EOT
// add the bin dir to the PATH to make local binaries of deps usable in scripts
$binDir = $composer->getConfig()->get('bin-dir');
if (is_dir($binDir)) {
putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
$_SERVER['PATH'] = realpath($binDir).PATH_SEPARATOR.getenv('PATH');
putenv('PATH='.$_SERVER['PATH']);
}
$args = $input->getArguments();

View File

@ -62,7 +62,7 @@ EOT
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
$output->writeln('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
$this->getIO()->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
$installedRepo = $platformRepo;
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
}
@ -78,7 +78,7 @@ EOT
$results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
foreach ($results as $result) {
$output->writeln($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
$this->getIO()->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
}
}
}

View File

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

View File

@ -84,7 +84,7 @@ EOT
} 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)));
$this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
}
} elseif ($composer) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
@ -92,7 +92,7 @@ EOT
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
$output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
$this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
$installedRepo = $platformRepo;
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
}
@ -119,9 +119,9 @@ EOT
$this->printLinks($input, $output, $package, 'requires');
$this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
if ($package->getSuggests()) {
$output->writeln("\n<info>suggests</info>");
$this->getIO()->write("\n<info>suggests</info>");
foreach ($package->getSuggests() as $suggested => $reason) {
$output->writeln($suggested . ' <comment>' . $reason . '</comment>');
$this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>');
}
}
$this->printLinks($input, $output, $package, 'provides');
@ -172,7 +172,7 @@ EOT
foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
if (isset($packages[$type])) {
if ($tree) {
$output->writeln($type);
$this->getIO()->write($type);
}
ksort($packages[$type]);
@ -222,10 +222,10 @@ EOT
} else {
$output->write($indent . $package);
}
$output->writeln('');
$this->getIO()->write('');
}
if ($tree) {
$output->writeln('');
$this->getIO()->write('');
}
}
}
@ -285,53 +285,53 @@ EOT
*/
protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
{
$output->writeln('<info>name</info> : ' . $package->getPrettyName());
$output->writeln('<info>descrip.</info> : ' . $package->getDescription());
$output->writeln('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
$this->getIO()->write('<info>name</info> : ' . $package->getPrettyName());
$this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
$this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
$this->printVersions($input, $output, $package, $versions, $installedRepo, $repos);
$output->writeln('<info>type</info> : ' . $package->getType());
$output->writeln('<info>license</info> : ' . implode(', ', $package->getLicense()));
$output->writeln('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
$output->writeln('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
$output->writeln('<info>names</info> : ' . implode(', ', $package->getNames()));
$this->getIO()->write('<info>type</info> : ' . $package->getType());
$this->getIO()->write('<info>license</info> : ' . implode(', ', $package->getLicense()));
$this->getIO()->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
$this->getIO()->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
$this->getIO()->write('<info>names</info> : ' . implode(', ', $package->getNames()));
if ($package->isAbandoned()) {
$replacement = ($package->getReplacementPackage() !== null)
? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
: null;
$output->writeln(
$this->getIO()->writeError(
sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement)
);
}
if ($package->getSupport()) {
$output->writeln("\n<info>support</info>");
$this->getIO()->write("\n<info>support</info>");
foreach ($package->getSupport() as $type => $value) {
$output->writeln('<comment>' . $type . '</comment> : '.$value);
$this->getIO()->write('<comment>' . $type . '</comment> : '.$value);
}
}
if ($package->getAutoload()) {
$output->writeln("\n<info>autoload</info>");
$this->getIO()->write("\n<info>autoload</info>");
foreach ($package->getAutoload() as $type => $autoloads) {
$output->writeln('<comment>' . $type . '</comment>');
$this->getIO()->write('<comment>' . $type . '</comment>');
if ($type === 'psr-0') {
foreach ($autoloads as $name => $path) {
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
$this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
}
} elseif ($type === 'psr-4') {
foreach ($autoloads as $name => $path) {
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
$this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
}
} elseif ($type === 'classmap') {
$output->writeln(implode(', ', $autoloads));
$this->getIO()->write(implode(', ', $autoloads));
}
}
if ($package->getIncludePaths()) {
$output->writeln('<comment>include-path</comment>');
$output->writeln(implode(', ', $package->getIncludePaths()));
$this->getIO()->write('<comment>include-path</comment>');
$this->getIO()->write(implode(', ', $package->getIncludePaths()));
}
}
}
@ -355,7 +355,7 @@ EOT
$versions = implode(', ', $versions);
$output->writeln('<info>versions</info> : ' . $versions);
$this->getIO()->write('<info>versions</info> : ' . $versions);
}
/**
@ -371,10 +371,10 @@ EOT
{
$title = $title ?: $linkType;
if ($links = $package->{'get'.ucfirst($linkType)}()) {
$output->writeln("\n<info>" . $title . "</info>");
$this->getIO()->write("\n<info>" . $title . "</info>");
foreach ($links as $link) {
$output->writeln($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
$this->getIO()->write($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
}
}
}

View File

@ -57,7 +57,7 @@ EOT
$im = $composer->getInstallationManager();
// Dispatch pre-status-command
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::PRE_STATUS_CMD, true);
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
$errors = array();
@ -76,9 +76,9 @@ EOT
// output errors/warnings
if (!$errors) {
$output->writeln('<info>No local changes</info>');
$this->getIO()->writeError('<info>No local changes</info>');
} else {
$output->writeln('<error>You have changes in the following dependencies:</error>');
$this->getIO()->writeError('<error>You have changes in the following dependencies:</error>');
}
foreach ($errors as $path => $changes) {
@ -86,19 +86,19 @@ EOT
$indentedChanges = implode("\n", array_map(function ($line) {
return ' ' . ltrim($line);
}, explode("\n", $changes)));
$output->writeln('<info>'.$path.'</info>:');
$output->writeln($indentedChanges);
$this->getIO()->write('<info>'.$path.'</info>:');
$this->getIO()->write($indentedChanges);
} else {
$output->writeln($path);
$this->getIO()->write($path);
}
}
if ($errors && !$input->getOption('verbose')) {
$output->writeln('Use --verbose (-v) to see modified files');
$this->getIO()->writeError('Use --verbose (-v) to see modified files');
}
// Dispatch post-status-command
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_STATUS_CMD, true);
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true);
return $errors ? 1 : 0;
}

View File

@ -75,12 +75,12 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('no-custom-installers')) {
$output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$output->writeln('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
$this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));

View File

@ -37,6 +37,7 @@ class ValidateCommand extends Command
->setDescription('Validates a composer.json')
->setDefinition(array(
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
))
->setHelp(<<<EOT
@ -57,45 +58,53 @@ EOT
$file = $input->getArgument('file');
if (!file_exists($file)) {
$output->writeln('<error>' . $file . ' not found.</error>');
$this->getIO()->writeError('<error>' . $file . ' not found.</error>');
return 1;
}
if (!is_readable($file)) {
$output->writeln('<error>' . $file . ' is not readable.</error>');
$this->getIO()->writeError('<error>' . $file . ' is not readable.</error>');
return 1;
}
$validator = new ConfigValidator($this->getIO());
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
$checkPublish = !$input->getOption('no-check-publish');
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
// output errors/warnings
if (!$errors && !$publishErrors && !$warnings) {
$output->writeln('<info>' . $file . ' is valid</info>');
$this->getIO()->write('<info>' . $file . ' is valid</info>');
} elseif (!$errors && !$publishErrors) {
$output->writeln('<info>' . $file . ' is valid, but with a few warnings</info>');
$output->writeln('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
$this->getIO()->writeError('<info>' . $file . ' is valid, but with a few warnings</info>');
$this->getIO()->writeError('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
} elseif (!$errors) {
$output->writeln('<info>' . $file . ' is valid for simple usage with composer but has</info>');
$output->writeln('<info>strict errors that make it unable to be published as a package:</info>');
$output->writeln('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
$this->getIO()->writeError('<info>' . $file . ' is valid for simple usage with composer but has</info>');
$this->getIO()->writeError('<info>strict errors that make it unable to be published as a package:</info>');
$this->getIO()->writeError('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
} else {
$output->writeln('<error>' . $file . ' is invalid, the following errors/warnings were found:</error>');
$this->getIO()->writeError('<error>' . $file . ' is invalid, the following errors/warnings were found:</error>');
}
$messages = array(
'error' => array_merge($errors, $publishErrors),
'error' => $errors,
'warning' => $warnings,
);
// If checking publish errors, display them errors, otherwise just show them as warnings
if ($checkPublish) {
$messages['error'] = array_merge($messages['error'], $publishErrors);
} else {
$messages['warning'] = array_merge($messages['warning'], $publishErrors);
}
foreach ($messages as $style => $msgs) {
foreach ($msgs as $msg) {
$output->writeln('<' . $style . '>' . $msg . '</' . $style . '>');
$this->getIO()->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
}
}
return $errors || $publishErrors ? 1 : 0;
return $errors || ($publishErrors && $checkPublish) ? 1 : 0;
}
}

View File

@ -102,10 +102,13 @@ class Compiler
$finder->files()
->ignoreVCS(true)
->name('*.php')
->name('LICENSE')
->exclude('Tests')
->exclude('tests')
->exclude('docs')
->in(__DIR__.'/../../vendor/symfony/')
->in(__DIR__.'/../../vendor/seld/jsonlint/src/')
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/src/')
->in(__DIR__.'/../../vendor/seld/jsonlint/')
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
;
foreach ($finder as $file) {

View File

@ -15,6 +15,7 @@ namespace Composer\Console;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Formatter\OutputFormatter;
@ -91,7 +92,7 @@ class Application extends BaseApplication
$this->io = new ConsoleIO($input, $output, $this->getHelperSet());
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$output->writeln('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
$this->getIO()->writeError('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
}
if (defined('COMPOSER_DEV_WARNING_TIME')) {
@ -104,7 +105,7 @@ class Application extends BaseApplication
}
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
if (time() > COMPOSER_DEV_WARNING_TIME) {
$output->writeln(sprintf('<warning>Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
$this->getIO()->writeError(sprintf('<warning>Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
}
}
}
@ -117,8 +118,8 @@ class Application extends BaseApplication
if ($newWorkDir = $this->getNewWorkingDir($input)) {
$oldWorkingDir = getcwd();
chdir($newWorkDir);
if ($output->getVerbosity() >= 4) {
$output->writeln('Changed CWD to ' . getcwd());
if ($this->getIO()->isDebug() >= 4) {
$this->getIO()->writeError('Changed CWD to ' . getcwd());
}
}
@ -129,7 +130,7 @@ class Application extends BaseApplication
foreach ($composer['scripts'] as $script => $dummy) {
if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
if ($this->has($script)) {
$output->writeln('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
$this->getIO()->writeError('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
} else {
$this->add(new Command\ScriptAliasCommand($script));
}
@ -150,7 +151,7 @@ class Application extends BaseApplication
}
if (isset($startTime)) {
$output->writeln('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
$this->getIO()->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
}
return $result;
@ -186,23 +187,27 @@ class Application extends BaseApplication
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|| (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
) {
$output->writeln('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
$this->getIO()->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
}
}
} catch (\Exception $e) {
}
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
$output->writeln('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
$output->writeln('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
$this->getIO()->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
$this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
}
if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
$output->writeln('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
$output->writeln('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
$this->getIO()->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
$this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
}
return parent::renderException($exception, $output);
if ($output instanceof ConsoleOutputInterface) {
parent::renderException($exception, $output->getErrorOutput());
} else {
parent::renderException($exception, $output);
}
}
/**
@ -218,7 +223,7 @@ class Application extends BaseApplication
$this->composer = Factory::create($this->io, null, $disablePlugins);
} catch (\InvalidArgumentException $e) {
if ($required) {
$this->io->write($e->getMessage());
$this->io->writeError($e->getMessage());
exit(1);
}
} catch (JsonValidationException $e) {
@ -323,7 +328,6 @@ class Application extends BaseApplication
protected function getDefaultHelperSet()
{
$helperSet = parent::getDefaultHelperSet();
$helperSet->set(new DialogHelper());
return $helperSet;

View File

@ -83,6 +83,6 @@ class HtmlOutputFormatter extends OutputFormatter
}
}
return $out . '">'.$matches[2].'</span>';
return $out.'">'.$matches[2].'</span>';
}
}

View File

@ -65,6 +65,11 @@ class Pool
}
$this->stabilityFlags = $stabilityFlags;
$this->filterRequires = $filterRequires;
foreach ($filterRequires as $name => $constraint) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
unset($this->filterRequires[$name]);
}
}
}
public function setWhitelist($whitelist)

View File

@ -87,6 +87,19 @@ class Problem
}
if ($job && $job['cmd'] === 'install' && empty($packages)) {
// handle php/hhvm
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
if (defined('HHVM_VERSION')) {
return $msg . 'your HHVM version does not satisfy that requirement.';
} elseif ($job['packageName'] === 'hhvm') {
return $msg . 'you are running this with PHP and not HHVM.';
}
return $msg . 'your PHP version does not satisfy that requirement.';
}
// handle php extensions
if (0 === stripos($job['packageName'], 'ext-')) {
$ext = substr($job['packageName'], 4);

View File

@ -217,7 +217,15 @@ class Rule
$targetName = $this->reasonData->getTarget();
// handle php extensions
if (0 === strpos($targetName, 'ext-')) {
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
if (defined('HHVM_VERSION')) {
$text .= ' -> your HHVM version does not satisfy that requirement.';
} elseif ($targetName === 'hhvm') {
$text .= ' -> you are running this with PHP and not HHVM.';
} else {
$text .= ' -> your PHP version does not satisfy that requirement.';
}
} elseif (0 === strpos($targetName, 'ext-')) {
$ext = substr($targetName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';

View File

@ -40,6 +40,7 @@ class Solver
protected $branches = array();
protected $problems = array();
protected $learnedPool = array();
protected $learnedWhy = array();
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
{

View File

@ -35,7 +35,7 @@ abstract class ArchiveDownloader extends FileDownloader
$fileName = parent::download($package, $path);
if ($this->io->isVerbose()) {
$this->io->write(' Extracting archive');
$this->io->writeError(' Extracting archive');
}
try {
@ -77,7 +77,7 @@ abstract class ArchiveDownloader extends FileDownloader
// retry downloading if we have an invalid zip file
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
$this->io->write(' Invalid zip file, retrying...');
$this->io->writeError(' Invalid zip file, retrying...');
usleep(500000);
continue;
}
@ -88,7 +88,7 @@ abstract class ArchiveDownloader extends FileDownloader
break;
}
$this->io->write('');
$this->io->writeError('');
}
/**

View File

@ -192,7 +192,7 @@ class DownloadManager
foreach ($sources as $i => $source) {
if (isset($e)) {
$this->io->write(' <warning>Now trying to download from ' . $source . '</warning>');
$this->io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
}
$package->setInstallationSource($source);
try {
@ -206,7 +206,7 @@ class DownloadManager
throw $e;
}
$this->io->write(
$this->io->writeError(
' <warning>Failed to download '.
$package->getPrettyName().
' from ' . $source . ': '.

View File

@ -81,7 +81,7 @@ class FileDownloader implements DownloaderInterface
throw new \InvalidArgumentException('The given package is missing url information');
}
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$urls = $package->getDistUrls();
while ($url = array_shift($urls)) {
@ -89,11 +89,11 @@ class FileDownloader implements DownloaderInterface
return $this->doDownload($package, $path, $url);
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('');
$this->io->write('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
$this->io->writeError('');
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write('');
$this->io->write(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')');
$this->io->writeError('');
$this->io->writeError(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')');
}
if (!count($urls)) {
@ -102,7 +102,7 @@ class FileDownloader implements DownloaderInterface
}
}
$this->io->write('');
$this->io->writeError('');
}
protected function doDownload(PackageInterface $package, $path, $url)
@ -127,7 +127,7 @@ class FileDownloader implements DownloaderInterface
// download if we don't have it in cache or the cache is invalidated
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
if (!$this->outputProgress) {
$this->io->write(' Downloading');
$this->io->writeError(' Downloading');
}
// try to download 3 times then fail hard
@ -142,7 +142,7 @@ class FileDownloader implements DownloaderInterface
throw $e;
}
if ($this->io->isVerbose()) {
$this->io->write(' Download failed, retrying...');
$this->io->writeError(' Download failed, retrying...');
}
usleep(500000);
}
@ -152,7 +152,7 @@ class FileDownloader implements DownloaderInterface
$this->cache->copyFrom($cacheKey, $fileName);
}
} else {
$this->io->write(' Loading from cache');
$this->io->writeError(' Loading from cache');
}
if (!file_exists($fileName)) {
@ -205,7 +205,7 @@ class FileDownloader implements DownloaderInterface
*/
public function remove(PackageInterface $package, $path)
{
$this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
if (!$this->filesystem->removeDirectory($path)) {
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
}

View File

@ -45,7 +45,7 @@ class GitDownloader extends VcsDownloader
$ref = $package->getSourceReference();
$flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : '';
$command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer';
$this->io->write(" Cloning ".$ref);
$this->io->writeError(" Cloning ".$ref);
$commandCallable = function ($url) use ($ref, $path, $command) {
return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref));
@ -78,7 +78,7 @@ class GitDownloader extends VcsDownloader
}
$ref = $target->getSourceReference();
$this->io->write(" Checking out ".$ref);
$this->io->writeError(" Checking out ".$ref);
$command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
$commandCallable = function ($url) use ($command) {
@ -143,10 +143,10 @@ class GitDownloader extends VcsDownloader
$changes = array_map(function ($elem) {
return ' '.$elem;
}, preg_split('{\s*\r?\n\s*}', $changes));
$this->io->write(' <error>The package has modified files:</error>');
$this->io->write(array_slice($changes, 0, 10));
$this->io->writeError(' <error>The package has modified files:</error>');
$this->io->writeError(array_slice($changes, 0, 10));
if (count($changes) > 10) {
$this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
$this->io->writeError(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
}
while (true) {
@ -167,21 +167,21 @@ class GitDownloader extends VcsDownloader
throw new \RuntimeException('Update aborted');
case 'v':
$this->io->write($changes);
$this->io->writeError($changes);
break;
case '?':
default:
help:
$this->io->write(array(
$this->io->writeError(array(
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
' v - view modified files',
));
if ($update) {
$this->io->write(' s - stash changes and try to reapply them after the update');
$this->io->writeError(' s - stash changes and try to reapply them after the update');
}
$this->io->write(' ? - print help');
$this->io->writeError(' ? - print help');
break;
}
}
@ -195,7 +195,7 @@ class GitDownloader extends VcsDownloader
$path = $this->normalizePath($path);
if ($this->hasStashedChanges) {
$this->hasStashedChanges = false;
$this->io->write(' <info>Re-applying stashed changes</info>');
$this->io->writeError(' <info>Re-applying stashed changes</info>');
if (0 !== $this->process->execute('git stash pop', $output, $path)) {
throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput());
}
@ -261,7 +261,7 @@ class GitDownloader extends VcsDownloader
// reference was not found (prints "fatal: reference is not a tree: $ref")
if (false !== strpos($this->process->getErrorOutput(), $reference)) {
$this->io->write(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
$this->io->writeError(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
}
throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput());

View File

@ -27,7 +27,7 @@ class HgDownloader extends VcsDownloader
{
$url = ProcessExecutor::escape($url);
$ref = ProcessExecutor::escape($package->getSourceReference());
$this->io->write(" Cloning ".$package->getSourceReference());
$this->io->writeError(" Cloning ".$package->getSourceReference());
$command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
if (0 !== $this->process->execute($command, $ignoredOutput)) {
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
@ -45,7 +45,7 @@ class HgDownloader extends VcsDownloader
{
$url = ProcessExecutor::escape($url);
$ref = ProcessExecutor::escape($target->getSourceReference());
$this->io->write(" Updating to ".$target->getSourceReference());
$this->io->writeError(" Updating to ".$target->getSourceReference());
if (!is_dir($path.'/.hg')) {
throw new \RuntimeException('The .hg directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');

View File

@ -31,7 +31,7 @@ class PerforceDownloader extends VcsDownloader
$ref = $package->getSourceReference();
$label = $this->getLabelFromSourceReference($ref);
$this->io->write(' Cloning ' . $ref);
$this->io->writeError(' Cloning ' . $ref);
$this->initPerforce($package, $path, $url);
$this->perforce->setStream($ref);
$this->perforce->p4Login($this->io);
@ -85,7 +85,7 @@ class PerforceDownloader extends VcsDownloader
*/
public function getLocalChanges(PackageInterface $package, $path)
{
$this->io->write('Perforce driver does not check for local changes before overriding', true);
$this->io->writeError('Perforce driver does not check for local changes before overriding', true);
return;
}

View File

@ -29,7 +29,7 @@ class SvnDownloader extends VcsDownloader
SvnUtil::cleanEnv();
$ref = $package->getSourceReference();
$this->io->write(" Checking out ".$package->getSourceReference());
$this->io->writeError(" Checking out ".$package->getSourceReference());
$this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path);
}
@ -52,7 +52,7 @@ class SvnDownloader extends VcsDownloader
}
}
$this->io->write(" Checking out " . $ref);
$this->io->writeError(" Checking out " . $ref);
$this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path);
}
@ -114,10 +114,10 @@ class SvnDownloader extends VcsDownloader
$changes = array_map(function ($elem) {
return ' '.$elem;
}, preg_split('{\s*\r?\n\s*}', $changes));
$this->io->write(' <error>The package has modified files:</error>');
$this->io->write(array_slice($changes, 0, 10));
$this->io->writeError(' <error>The package has modified files:</error>');
$this->io->writeError(array_slice($changes, 0, 10));
if (count($changes) > 10) {
$this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
$this->io->writeError(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
}
while (true) {
@ -130,12 +130,12 @@ class SvnDownloader extends VcsDownloader
throw new \RuntimeException('Update aborted');
case 'v':
$this->io->write($changes);
$this->io->writeError($changes);
break;
case '?':
default:
$this->io->write(array(
$this->io->writeError(array(
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
' v - view modified files',

View File

@ -54,7 +54,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
}
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$this->filesystem->emptyDirectory($path);
$urls = $package->getSourceUrls();
@ -67,9 +67,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
break;
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL');
$this->io->writeError(' Failed, trying the next URL');
}
if (!count($urls)) {
throw $e;
@ -77,7 +77,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
}
}
$this->io->write('');
$this->io->writeError('');
}
/**
@ -104,7 +104,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
$to = VersionParser::formatVersion($target);
}
$this->io->write(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
$this->io->writeError(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
$this->cleanChanges($initial, $path, true);
$urls = $target->getSourceUrls();
@ -117,9 +117,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
break;
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL');
$this->io->writeError(' Failed, trying the next URL');
} else {
// in case of failed update, try to reapply the changes before aborting
$this->reapplyChanges($path);
@ -146,12 +146,12 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
return ' ' . $line;
}, explode("\n", $logs)));
$this->io->write(' '.$message);
$this->io->write($logs);
$this->io->writeError(' '.$message);
$this->io->writeError($logs);
}
}
$this->io->write('');
$this->io->writeError('');
}
/**
@ -159,7 +159,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
*/
public function remove(PackageInterface $package, $path)
{
$this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$this->cleanChanges($package, $path, false);
if (!$this->filesystem->removeDirectory($path)) {
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');

View File

@ -21,7 +21,6 @@ use Composer\Composer;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\Repository\CompositeRepository;
use Composer\Script;
use Composer\Script\CommandEvent;
use Composer\Script\PackageEvent;
use Composer\Util\ProcessExecutor;
@ -95,36 +94,28 @@ class EventDispatcher
/**
* Dispatch a package event.
*
* @param string $eventName The constant in ScriptEvents
* @param boolean $devMode Whether or not we are in dev mode
* @param OperationInterface $operation The package being installed/updated/removed
* @return int return code of the executed script if any, for php scripts a false return
* value is changed to 1, anything else to 0
*/
public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation)
{
return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
}
/**
* Dispatch a command event.
* @param string $eventName The constant in PackageEvents
* @param bool $devMode Whether or not we are in dev mode
* @param PolicyInterface $policy The policy
* @param Pool $pool The pool
* @param CompositeRepository $installedRepo The installed repository
* @param Request $request The request
* @param array $operations The list of operations
* @param OperationInterface $operation The package being installed/updated/removed
*
* @param string $eventName The constant in ScriptEvents
* @param boolean $devMode Whether or not we are in dev mode
* @param array $additionalArgs Arguments passed by the user
* @param array $flags Optional flags to pass data not as argument
* @return int return code of the executed script if any, for php scripts a false return
* value is changed to 1, anything else to 0
* @return int return code of the executed script if any, for php scripts a false return
* value is changed to 1, anything else to 0
*/
public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array(), $flags = array())
public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
{
return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags));
return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations, $operation));
}
/**
* Dispatch a installer event.
*
* @param string $eventName The constant in InstallerEvents
* @param bool $devMode Whether or not we are in dev mode
* @param PolicyInterface $policy The policy
* @param Pool $pool The pool
* @param CompositeRepository $installedRepo The installed repository
@ -134,9 +125,9 @@ class EventDispatcher
* @return int return code of the executed script if any, for php scripts a false return
* value is changed to 1, anything else to 0
*/
public function dispatchInstallerEvent($eventName, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
{
return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $policy, $pool, $installedRepo, $request, $operations));
return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations));
}
/**
@ -163,11 +154,11 @@ class EventDispatcher
$methodName = substr($callable, strpos($callable, '::') + 2);
if (!class_exists($className)) {
$this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
$this->io->writeError('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
continue;
}
if (!is_callable($callable)) {
$this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
$this->io->writeError('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
continue;
}
@ -175,13 +166,13 @@ class EventDispatcher
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
} catch (\Exception $e) {
$message = "Script %s handling the %s event terminated with an exception";
$this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
$this->io->writeError('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
throw $e;
}
} else {
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments()));
if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) {
$this->io->write(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
}
@ -214,10 +205,6 @@ class EventDispatcher
*/
protected function checkListenerExpectedEvent($target, Event $event)
{
if (!$event instanceof Script\Event) {
return $event;
}
try {
$reflected = new \ReflectionParameter($target, 0);
} catch (\Exception $e) {
@ -232,8 +219,24 @@ class EventDispatcher
$expected = $typehint->getName();
// BC support
if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') {
$event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments());
$event = new \Composer\Script\CommandEvent(
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments()
);
}
if (!$event instanceof $expected && $expected === 'Composer\Script\PackageEvent') {
$event = new \Composer\Script\PackageEvent(
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(),
$event->getPolicy(), $event->getPool(), $event->getInstalledRepo(), $event->getRequest(),
$event->getOperations(), $event->getOperation()
);
}
if (!$event instanceof $expected && $expected === 'Composer\Script\Event') {
$event = new \Composer\Script\Event(
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(),
$event->getArguments(), $event->getFlags()
);
}
return $event;

View File

@ -113,10 +113,10 @@ class Factory
$config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir)));
// load global config
$file = new JsonFile($home.'/config.json');
$file = new JsonFile($config->get('home').'/config.json');
if ($file->exists()) {
if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $file->getPath());
$io->writeError('Loading config file ' . $file->getPath());
}
$config->merge($file->read());
}
@ -126,7 +126,7 @@ class Factory
$file = new JsonFile($config->get('home').'/auth.json');
if ($file->exists()) {
if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $file->getPath());
$io->writeError('Loading config file ' . $file->getPath());
}
$config->merge(array('config' => $file->read()));
}
@ -227,12 +227,12 @@ class Factory
$config->merge($localConfig);
if (isset($composerFile)) {
if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $composerFile);
$io->writeError('Loading config file ' . $composerFile);
}
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
if ($localAuthFile->exists()) {
if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $localAuthFile->getPath());
$io->writeError('Loading config file ' . $localAuthFile->getPath());
}
$config->merge(array('config' => $localAuthFile->read()));
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
@ -362,7 +362,7 @@ class Factory
$composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
} catch (\Exception $e) {
if ($io->isDebug()) {
$io->write('Failed to initialize global composer: '.$e->getMessage());
$io->writeError('Failed to initialize global composer: '.$e->getMessage());
}
}

View File

@ -23,16 +23,19 @@ use Symfony\Component\Console\Helper\HelperSet;
class BufferIO extends ConsoleIO
{
/**
* @param string $input
* @param int $verbosity
* @param string $input
* @param int $verbosity
* @param OutputFormatterInterface $formatter
*/
public function __construct($input = '', $verbosity = null, OutputFormatterInterface $formatter = null)
{
public function __construct(
$input = '',
$verbosity = StreamOutput::VERBOSITY_NORMAL,
OutputFormatterInterface $formatter = null
) {
$input = new StringInput($input);
$input->setInteractive(false);
$output = new StreamOutput(fopen('php://memory', 'rw'), $verbosity === null ? StreamOutput::VERBOSITY_NORMAL : $verbosity, !empty($formatter), $formatter);
$output = new StreamOutput(fopen('php://memory', 'rw'), $verbosity, !empty($formatter), $formatter);
parent::__construct($input, $output, new HelperSet(array()));
}

View File

@ -13,6 +13,7 @@
namespace Composer\IO;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Process\ExecutableFinder;
@ -29,6 +30,7 @@ class ConsoleIO extends BaseIO
protected $output;
protected $helperSet;
protected $lastMessage;
protected $lastMessageErr;
private $startTime;
/**
@ -94,6 +96,24 @@ class ConsoleIO extends BaseIO
* {@inheritDoc}
*/
public function write($messages, $newline = true)
{
$this->doWrite($messages, $newline, false);
}
/**
* {@inheritDoc}
*/
public function writeError($messages, $newline = true)
{
$this->doWrite($messages, $newline, true);
}
/**
* @param array $messages
* @param boolean $newline
* @param boolean $stderr
*/
private function doWrite($messages, $newline, $stderr)
{
if (null !== $this->startTime) {
$memoryUsage = memory_get_usage() / 1024 / 1024;
@ -102,6 +122,13 @@ class ConsoleIO extends BaseIO
return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message);
}, (array) $messages);
}
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
$this->output->getErrorOutput()->write($messages, $newline);
$this->lastMessageErr = join($newline ? "\n" : '', (array) $messages);
return;
}
$this->output->write($messages, $newline);
$this->lastMessage = join($newline ? "\n" : '', (array) $messages);
}
@ -111,12 +138,29 @@ class ConsoleIO extends BaseIO
*/
public function overwrite($messages, $newline = true, $size = null)
{
if (!$this->output->isDecorated()) {
if (!$messages) {
return;
}
$this->doOverwrite($messages, $newline, $size, false);
}
return $this->write($messages, count($messages) === 1 || $newline);
/**
* {@inheritDoc}
*/
public function overwriteError($messages, $newline = true, $size = null)
{
$this->doOverwrite($messages, $newline, $size, true);
}
/**
* @param array $messages
* @param boolean $newline
* @param integer $size
* @param boolean $stderr
*/
private function doOverwrite($messages, $newline, $size, $stderr)
{
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
$output = $this->output->getErrorOutput();
} else {
$output = $this->output;
}
// messages can be an array, let's convert it to string anyway
@ -125,26 +169,31 @@ class ConsoleIO extends BaseIO
// since overwrite is supposed to overwrite last message...
if (!isset($size)) {
// removing possible formatting of lastMessage with strip_tags
$size = strlen(strip_tags($this->lastMessage));
$size = strlen(strip_tags($stderr ? $this->lastMessageErr : $this->lastMessage));
}
// ...let's fill its length with backspaces
$this->write(str_repeat("\x08", $size), false);
$this->doWrite(str_repeat("\x08", $size), false, $stderr);
// write the new message
$this->write($messages, false);
$this->doWrite($messages, false, $stderr);
$fill = $size - strlen(strip_tags($messages));
if ($fill > 0) {
// whitespace whatever has left
$this->write(str_repeat(' ', $fill), false);
$this->doWrite(str_repeat(' ', $fill), false, $stderr);
// move the cursor back
$this->write(str_repeat("\x08", $fill), false);
$this->doWrite(str_repeat("\x08", $fill), false, $stderr);
}
if ($newline) {
$this->write('');
$this->doWrite('', true, $stderr);
}
if ($stderr) {
$this->lastMessageErr = $messages;
} else {
$this->lastMessage = $messages;
}
$this->lastMessage = $messages;
}
/**
@ -152,7 +201,16 @@ class ConsoleIO extends BaseIO
*/
public function ask($question, $default = null)
{
return $this->helperSet->get('dialog')->ask($this->output, $question, $default);
$output = $this->output;
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
$dialog = $this->helperSet->get('dialog');
return $dialog->ask($output, $question, $default);
}
/**
@ -160,7 +218,16 @@ class ConsoleIO extends BaseIO
*/
public function askConfirmation($question, $default = true)
{
return $this->helperSet->get('dialog')->askConfirmation($this->output, $question, $default);
$output = $this->output;
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
$dialog = $this->helperSet->get('dialog');
return $dialog->askConfirmation($output, $question, $default);
}
/**
@ -168,7 +235,16 @@ class ConsoleIO extends BaseIO
*/
public function askAndValidate($question, $validator, $attempts = false, $default = null)
{
return $this->helperSet->get('dialog')->askAndValidate($this->output, $question, $validator, $attempts, $default);
$output = $this->output;
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
$dialog = $this->helperSet->get('dialog');
return $dialog->askAndValidate($output, $question, $validator, $attempts, $default);
}
/**
@ -182,9 +258,9 @@ class ConsoleIO extends BaseIO
// use bash if it's present
if ($finder->find('bash') && $finder->find('stty')) {
$this->write($question, false);
$this->writeError($question, false);
$value = rtrim(shell_exec('bash -c "stty -echo; read -n0 discard; read -r mypassword; stty echo; echo $mypassword"'));
$this->write('');
$this->writeError('');
return $value;
}
@ -208,9 +284,9 @@ class ConsoleIO extends BaseIO
$exe = $tmpExe;
}
$this->write($question, false);
$this->writeError($question, false);
$value = rtrim(shell_exec($exe));
$this->write('');
$this->writeError('');
// clean up
if (isset($tmpExe)) {
@ -230,11 +306,11 @@ class ConsoleIO extends BaseIO
}
}
if (isset($shell)) {
$this->write($question, false);
$this->writeError($question, false);
$readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command));
$this->write('');
$this->writeError('');
return $value;
}

View File

@ -64,6 +64,14 @@ interface IOInterface
*/
public function write($messages, $newline = true);
/**
* Writes a message to the error output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
*/
public function writeError($messages, $newline = true);
/**
* Overwrites a previous message to the output.
*
@ -73,6 +81,15 @@ interface IOInterface
*/
public function overwrite($messages, $newline = true, $size = null);
/**
* Overwrites a previous message to the error output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param bool $newline Whether to add a newline or not
* @param integer $size The size of line
*/
public function overwriteError($messages, $newline = true, $size = null);
/**
* Asks a question to the user.
*

View File

@ -66,6 +66,13 @@ class NullIO extends BaseIO
{
}
/**
* {@inheritDoc}
*/
public function writeError($messages, $newline = true)
{
}
/**
* {@inheritDoc}
*/
@ -73,6 +80,13 @@ class NullIO extends BaseIO
{
}
/**
* {@inheritDoc}
*/
public function overwriteError($messages, $newline = true, $size = 80)
{
}
/**
* {@inheritDoc}
*/

View File

@ -177,7 +177,7 @@ class Installer
// purge old require-dev packages to avoid conflicts with the new way of handling dev requirements
$devRepo = new InstalledFilesystemRepository(new JsonFile($this->config->get('vendor-dir').'/composer/installed_dev.json'));
if ($devRepo->getPackages()) {
$this->io->write('<warning>BC Notice: Removing old dev packages to migrate to the new require-dev handling.</warning>');
$this->io->writeError('<warning>BC Notice: Removing old dev packages to migrate to the new require-dev handling.</warning>');
foreach ($devRepo->getPackages() as $package) {
if ($this->installationManager->isPackageInstalled($devRepo, $package)) {
$this->installationManager->uninstall($devRepo, new UninstallOperation($package));
@ -191,7 +191,7 @@ class Installer
if ($this->runScripts) {
// dispatch pre event
$eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
$this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
$this->eventDispatcher->dispatchScript($eventName, $this->devMode);
}
$this->downloadManager->setPreferSource($this->preferSource);
@ -243,7 +243,7 @@ class Installer
}
}
$this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')');
$this->io->writeError($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')');
}
}
@ -257,7 +257,7 @@ class Installer
? 'Use ' . $package->getReplacementPackage() . ' instead'
: 'No replacement was suggested';
$this->io->write(
$this->io->writeError(
sprintf(
"<error>Package %s is abandoned, you should avoid using it. %s.</error>",
$package->getPrettyName(),
@ -288,10 +288,10 @@ class Installer
$request->install($link->getTarget(), $link->getConstraint());
}
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request);
$solver = new Solver($policy, $pool, $installedRepo);
$ops = $solver->solve($request, $this->ignorePlatformReqs);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $ops);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
foreach ($ops as $op) {
if ($op->getJobType() === 'uninstall') {
$devPackages[] = $op->getPackage();
@ -314,16 +314,16 @@ class Installer
$this->preferLowest
);
if ($updatedLock) {
$this->io->write('<info>Writing lock file</info>');
$this->io->writeError('<info>Writing lock file</info>');
}
}
if ($this->dumpAutoloader) {
// write autoloader
if ($this->optimizeAutoloader) {
$this->io->write('<info>Generating optimized autoload files</info>');
$this->io->writeError('<info>Generating optimized autoload files</info>');
} else {
$this->io->write('<info>Generating autoload files</info>');
$this->io->writeError('<info>Generating autoload files</info>');
}
$this->autoloadGenerator->setDevMode($this->devMode);
@ -333,7 +333,7 @@ class Installer
if ($this->runScripts) {
// dispatch post event
$eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
$this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
$this->eventDispatcher->dispatchScript($eventName, $this->devMode);
}
$vendorDir = $this->config->get('vendor-dir');
@ -374,11 +374,11 @@ class Installer
$this->package->getDevRequires()
);
$this->io->write('<info>Loading composer repositories with package information</info>');
$this->io->writeError('<info>Loading composer repositories with package information</info>');
// creating repository pool
$policy = $this->createPolicy();
$pool = $this->createPool($withDevReqs);
$pool = $this->createPool($withDevReqs, $lockedRepository);
$pool->addRepository($installedRepo, $aliases);
if ($installFromLock) {
$pool->addRepository($lockedRepository, $aliases);
@ -409,7 +409,7 @@ class Installer
}
if ($this->update) {
$this->io->write('<info>Updating dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
$this->io->writeError('<info>Updating dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
$request->updateAll();
@ -460,10 +460,10 @@ class Installer
}
}
} elseif ($installFromLock) {
$this->io->write('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').' from lock file</info>');
$this->io->writeError('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').' from lock file</info>');
if (!$this->locker->isFresh()) {
$this->io->write('<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.</warning>');
$this->io->writeError('<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.</warning>');
}
foreach ($lockedRepository->getPackages() as $package) {
@ -480,7 +480,7 @@ class Installer
$request->install($link->getTarget(), $link->getConstraint());
}
} else {
$this->io->write('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
$this->io->writeError('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
if ($withDevReqs) {
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
@ -497,14 +497,14 @@ class Installer
$this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links');
// solve dependencies
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request);
$solver = new Solver($policy, $pool, $installedRepo);
try {
$operations = $solver->solve($request, $this->ignorePlatformReqs);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $operations);
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request, $operations);
} catch (SolverProblemsException $e) {
$this->io->write('<error>Your requirements could not be resolved to an installable set of packages.</error>');
$this->io->write($e->getMessage());
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>');
$this->io->writeError($e->getMessage());
return max(1, $e->getCode());
}
@ -514,7 +514,7 @@ class Installer
// execute operations
if (!$operations) {
$this->io->write('Nothing to install or update');
$this->io->writeError('Nothing to install or update');
}
$operations = $this->movePluginsToFront($operations);
@ -553,26 +553,26 @@ class Installer
&& $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference()
) {
if ($this->io->isDebug()) {
$this->io->write(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
$this->io->write('');
$this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
$this->io->writeError('');
}
continue;
}
}
$event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType());
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType());
if (defined($event) && $this->runScripts) {
$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation);
$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation);
}
// output non-alias ops in dry run, output alias ops in debug verbosity
if ($this->dryRun && false === strpos($operation->getJobType(), 'Alias')) {
$this->io->write(' - ' . $operation);
$this->io->write('');
$this->io->writeError(' - ' . $operation);
$this->io->writeError('');
} elseif ($this->io->isDebug() && false !== strpos($operation->getJobType(), 'Alias')) {
$this->io->write(' - ' . $operation);
$this->io->write('');
$this->io->writeError(' - ' . $operation);
$this->io->writeError('');
}
$this->installationManager->execute($localRepo, $operation);
@ -583,20 +583,20 @@ class Installer
if ($reason instanceof Rule) {
switch ($reason->getReason()) {
case Rule::RULE_JOB_INSTALL:
$this->io->write(' REASON: Required by root: '.$reason->getPrettyString($pool));
$this->io->write('');
$this->io->writeError(' REASON: Required by root: '.$reason->getPrettyString($pool));
$this->io->writeError('');
break;
case Rule::RULE_PACKAGE_REQUIRES:
$this->io->write(' REASON: '.$reason->getPrettyString($pool));
$this->io->write('');
$this->io->writeError(' REASON: '.$reason->getPrettyString($pool));
$this->io->writeError('');
break;
}
}
}
$event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());
if (defined($event) && $this->runScripts) {
$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation);
$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation);
}
if (!$this->dryRun) {
@ -671,27 +671,39 @@ class Installer
return array_merge($uninstOps, $operations);
}
private function createPool($withDevReqs)
private function createPool($withDevReqs, RepositoryInterface $lockedRepository = null)
{
$minimumStability = $this->package->getMinimumStability();
$stabilityFlags = $this->package->getStabilityFlags();
if (!$this->update && $this->locker->isLocked()) {
if (!$this->update && $this->locker->isLocked()) { // install from lock
$minimumStability = $this->locker->getMinimumStability();
$stabilityFlags = $this->locker->getStabilityFlags();
$requires = array();
foreach ($lockedRepository->getPackages() as $package) {
$constraint = new VersionConstraint('=', $package->getVersion());
$constraint->setPrettyString($package->getPrettyVersion());
$requires[$package->getName()] = $constraint;
}
} else {
$minimumStability = $this->package->getMinimumStability();
$stabilityFlags = $this->package->getStabilityFlags();
$requires = $this->package->getRequires();
if ($withDevReqs) {
$requires = array_merge($requires, $this->package->getDevRequires());
}
}
$requires = $this->package->getRequires();
if ($withDevReqs) {
$requires = array_merge($requires, $this->package->getDevRequires());
}
$rootConstraints = array();
foreach ($requires as $req => $constraint) {
// skip platform requirements from the root package to avoid filtering out existing platform packages
if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
continue;
}
$rootConstraints[$req] = $constraint->getConstraint();
if ($constraint instanceof Link) {
$rootConstraints[$req] = $constraint->getConstraint();
} else {
$rootConstraints[$req] = $constraint;
}
}
return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
@ -989,7 +1001,7 @@ class Installer
}
if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock'))) {
$this->io->write('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
$this->io->writeError('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
}
foreach ($depPackages as $depPackage) {
@ -1166,7 +1178,6 @@ class Installer
return $this;
}
/**
* set whether to run autoloader or not
*
@ -1180,7 +1191,6 @@ class Installer
return $this;
}
/**
* set whether to run scripts or not
*

View File

@ -38,6 +38,11 @@ class InstallerEvent extends Event
*/
private $io;
/**
* @var bool
*/
private $devMode;
/**
* @var PolicyInterface
*/
@ -69,18 +74,20 @@ class InstallerEvent extends Event
* @param string $eventName
* @param Composer $composer
* @param IOInterface $io
* @param bool $devMode
* @param PolicyInterface $policy
* @param Pool $pool
* @param CompositeRepository $installedRepo
* @param Request $request
* @param OperationInterface[] $operations
*/
public function __construct($eventName, Composer $composer, IOInterface $io, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
{
parent::__construct($eventName);
$this->composer = $composer;
$this->io = $io;
$this->devMode = $devMode;
$this->policy = $policy;
$this->pool = $pool;
$this->installedRepo = $installedRepo;
@ -104,6 +111,14 @@ class InstallerEvent extends Event
return $this->io;
}
/**
* @return bool
*/
public function isDevMode()
{
return $this->devMode;
}
/**
* @return PolicyInterface
*/

View File

@ -197,7 +197,7 @@ class LibraryInstaller implements InstallerInterface
foreach ($binaries as $bin) {
$binPath = $this->getInstallPath($package).'/'.$bin;
if (!file_exists($binPath)) {
$this->io->write(' <warning>Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package</warning>');
$this->io->writeError(' <warning>Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package</warning>');
continue;
}
@ -216,7 +216,7 @@ class LibraryInstaller implements InstallerInterface
// is a fresh install of the vendor.
@chmod($link, 0777 & ~umask());
}
$this->io->write(' Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
$this->io->writeError(' Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
continue;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
@ -226,7 +226,7 @@ class LibraryInstaller implements InstallerInterface
@chmod($link, 0777 & ~umask());
$link .= '.bat';
if (file_exists($link)) {
$this->io->write(' Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
$this->io->writeError(' Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
}
}
if (!file_exists($link)) {

View File

@ -0,0 +1,66 @@
<?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\IO\IOInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\PolicyInterface;
use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Request;
use Composer\EventDispatcher\Event;
use Composer\Repository\CompositeRepository;
/**
* The Package Event.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PackageEvent extends InstallerEvent
{
/**
* @var OperationInterface The package instance
*/
private $operation;
/**
* Constructor.
*
* @param string $eventName
* @param Composer $composer
* @param IOInterface $io
* @param bool $devMode
* @param PolicyInterface $policy
* @param Pool $pool
* @param CompositeRepository $installedRepo
* @param Request $request
* @param OperationInterface[] $operations
* @param OperationInterface $operation
*/
public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
{
parent::__construct($eventName, $composer, $io, $devMode, $policy, $pool, $installedRepo, $request, $operations);
$this->operation = $operation;
}
/**
* Returns the package instance.
*
* @return OperationInterface
*/
public function getOperation()
{
return $this->operation;
}
}

View File

@ -0,0 +1,75 @@
<?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;
/**
* Package Events.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PackageEvents
{
/**
* The PRE_PACKAGE_INSTALL event occurs before a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_INSTALL = 'pre-package-install';
/**
* The POST_PACKAGE_INSTALL event occurs after a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_INSTALL = 'post-package-install';
/**
* The PRE_PACKAGE_UPDATE event occurs before a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UPDATE = 'pre-package-update';
/**
* The POST_PACKAGE_UPDATE event occurs after a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UPDATE = 'post-package-update';
/**
* The PRE_PACKAGE_UNINSTALL event occurs before a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall';
/**
* The POST_PACKAGE_UNINSTALL event occurs after a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UNINSTALL = 'post-package-uninstall';
}

View File

@ -76,7 +76,7 @@ class PearInstaller extends LibraryInstaller
$pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin', 'data' => '/data'), $vars);
if ($this->io->isVerbose()) {
$this->io->write(' Cleaning up');
$this->io->writeError(' Cleaning up');
}
$this->filesystem->unlink($packageArchive);
}

View File

@ -184,6 +184,9 @@ class JsonFile
{
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$json = json_encode($data, $options);
if (false === $json) {
self::throwEncodeError(json_last_error());
}
// compact brackets to follow recent php versions
if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(phpversion('json'), '1.3.6', '<'))) {
@ -195,6 +198,9 @@ class JsonFile
}
$json = json_encode($data);
if (false === $json) {
self::throwEncodeError(json_last_error());
}
$prettyPrint = (bool) ($options & self::JSON_PRETTY_PRINT);
$unescapeUnicode = (bool) ($options & self::JSON_UNESCAPED_UNICODE);
@ -209,6 +215,34 @@ class JsonFile
return $result;
}
/**
* Throws an exception according to a given code with a customized message
*
* @param int $code return code of json_last_error function
* @throws \RuntimeException
*/
private static function throwEncodeError($code)
{
switch ($code) {
case JSON_ERROR_DEPTH:
$msg = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$msg = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$msg = 'Unexpected control character found';
break;
case JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$msg = 'Unknown error';
}
throw new \RuntimeException('JSON encoding failed: '.$msg);
}
/**
* Parses json string and returns hash.
*
@ -219,6 +253,9 @@ class JsonFile
*/
public static function parseJson($json, $file = null)
{
if (null === $json) {
return;
}
$data = json_decode($json, true);
if (null === $data && JSON_ERROR_NONE !== json_last_error()) {
self::validateSyntax($json, $file);

View File

@ -249,7 +249,7 @@ class ArrayLoader implements LoaderInterface
}
// If using numeric aliases ensure the alias is a valid subversion
if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
if (($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
&& ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch))
&& (stripos($targetPrefix, $sourcePrefix) !== 0)
) {

View File

@ -277,7 +277,18 @@ class RootPackageLoader extends ArrayLoader
) {
$branch = preg_replace('{^dev-}', '', $version);
$length = PHP_INT_MAX;
$nonFeatureBranches = '';
if (!empty($config['non-feature-branches'])) {
$nonFeatureBranches = implode('|', $config['non-feature-branches']);
}
foreach ($branches as $candidate) {
// return directly, if branch is configured to be non-feature branch
if ($candidate === $branch && preg_match('{^(' . $nonFeatureBranches . ')$}', $candidate)) {
return $version;
}
// do not compare against other feature branches
if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
continue;

View File

@ -256,7 +256,7 @@ class ValidatingArrayLoader implements LoaderInterface
}
// If using numeric aliases ensure the alias is a valid subversion
if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
if (($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
&& ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch))
&& (stripos($targetPrefix, $sourcePrefix) !== 0)
) {

View File

@ -258,7 +258,10 @@ class Locker
$lock['packages-dev'] = $this->lockPackages($devPackages);
}
if (empty($lock['packages']) && empty($lock['packages-dev'])) {
$lock['platform'] = $platformReqs;
$lock['platform-dev'] = $platformDevReqs;
if (empty($lock['packages']) && empty($lock['packages-dev']) && empty($lock['platform']) && empty($lock['platform-dev'])) {
if ($this->lockFile->exists()) {
unlink($this->lockFile->getPath());
}
@ -266,9 +269,6 @@ class Locker
return false;
}
$lock['platform'] = $platformReqs;
$lock['platform-dev'] = $platformDevReqs;
if (!$this->isLocked() || $lock !== $this->getLockData()) {
$this->lockFile->write($lock);
$this->lockDataCache = null;

View File

@ -20,7 +20,7 @@ namespace Composer\Package;
interface RootPackageInterface extends CompletePackageInterface
{
/**
* Returns a set of package names and theirs aliases
* Returns a set of package names and their aliases
*
* @return array
*/

View File

@ -79,6 +79,9 @@ class PluginManager
*/
public function addPlugin(PluginInterface $plugin)
{
if ($this->io->isDebug()) {
$this->io->writeError('Loading plugin '.get_class($plugin));
}
$this->plugins[] = $plugin;
$plugin->activate($this->composer, $this->io);
@ -127,7 +130,7 @@ class PluginManager
}
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->io->writeError("<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);

View File

@ -60,14 +60,14 @@ class ArtifactRepository extends ArrayRepository
$package = $this->getComposerInformation($file);
if (!$package) {
if ($io->isVerbose()) {
$io->write("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package");
$io->writeError("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package");
}
continue;
}
if ($io->isVerbose()) {
$template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>';
$io->write(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()));
$io->writeError(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()));
}
$this->addPackage($package);

View File

@ -434,7 +434,7 @@ class ComposerRepository extends ArrayRepository
}
if (!empty($data['warning'])) {
$this->io->write('<warning>Warning from '.$this->url.': '.$data['warning'].'</warning>');
$this->io->writeError('<warning>Warning from '.$this->url.': '.$data['warning'].'</warning>');
}
if (!empty($data['providers-lazy-url'])) {
@ -613,8 +613,8 @@ class ComposerRepository extends ArrayRepository
if ($cacheKey && ($contents = $this->cache->read($cacheKey))) {
if (!$this->degradedMode) {
$this->io->write('<warning>'.$e->getMessage().'</warning>');
$this->io->write('<warning>'.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>');
$this->io->writeError('<warning>'.$e->getMessage().'</warning>');
$this->io->writeError('<warning>'.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>');
}
$this->degradedMode = true;
$data = JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey);

View File

@ -66,13 +66,13 @@ class PearRepository extends ArrayRepository
{
parent::initialize();
$this->io->write('Initializing PEAR repository '.$this->url);
$this->io->writeError('Initializing PEAR repository '.$this->url);
$reader = new ChannelReader($this->rfs);
try {
$channelInfo = $reader->read($this->url);
} catch (\Exception $e) {
$this->io->write('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
$this->io->writeError('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
return;
}
@ -98,7 +98,7 @@ class PearRepository extends ArrayRepository
$normalizedVersion = $versionParser->normalize($version);
} catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) {
$this->io->write('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage());
$this->io->writeError('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage());
}
continue;
}

View File

@ -149,7 +149,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->write('Skipping Bitbucket git driver for '.$url.' because the OpenSSL PHP extension is missing.');
$io->writeError('Skipping Bitbucket git driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
return false;

View File

@ -66,7 +66,7 @@ class GitDriver extends VcsDriver
};
$gitUtil->runCommand($commandCallable, $this->url, $this->repoDir);
} catch (\Exception $e) {
$this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$e->getMessage().')</error>');
$this->io->writeError('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$e->getMessage().')</error>');
}
} else {
// clean up directory and do a fresh clone into it
@ -242,7 +242,7 @@ class GitDriver extends VcsDriver
}
$process = new ProcessExecutor($io);
if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) {
if ($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0) {
return true;
}

View File

@ -266,7 +266,7 @@ class GitHubDriver extends VcsDriver
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->write('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.');
$io->writeError('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
return false;
@ -333,7 +333,7 @@ class GitHubDriver extends VcsDriver
if (!$this->io->hasAuthentication($this->originUrl)) {
if (!$this->io->isInteractive()) {
$this->io->write('<error>GitHub API limit exhausted. Failed to get metadata for the '.$this->url.' repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>');
$this->io->writeError('<error>GitHub API limit exhausted. Failed to get metadata for the '.$this->url.' repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>');
throw $e;
}
@ -344,7 +344,7 @@ class GitHubDriver extends VcsDriver
if ($rateLimited) {
$rateLimit = $this->getRateLimit($e->getHeaders());
$this->io->write(sprintf(
$this->io->writeError(sprintf(
'<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
$rateLimit['limit'],
$rateLimit['reset']
@ -435,7 +435,7 @@ class GitHubDriver extends VcsDriver
} catch (\RuntimeException $e) {
$this->gitDriver = null;
$this->io->write('<error>Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your GitHub credentials</error>');
$this->io->writeError('<error>Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your GitHub credentials</error>');
throw $e;
}
}

View File

@ -159,7 +159,7 @@ class HgBitbucketDriver extends VcsDriver
if (!extension_loaded('openssl')) {
if ($io->isVerbose()) {
$io->write('Skipping Bitbucket hg driver for '.$url.' because the OpenSSL PHP extension is missing.');
$io->writeError('Skipping Bitbucket hg driver for '.$url.' because the OpenSSL PHP extension is missing.');
}
return false;

View File

@ -50,7 +50,7 @@ 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', $output, $this->repoDir)) {
$this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')</error>');
$this->io->writeError('<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

View File

@ -127,16 +127,16 @@ class VcsRepository extends ArrayRepository
}
} catch (\Exception $e) {
if ($verbose) {
$this->io->write('<error>Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage().'</error>');
$this->io->writeError('<error>Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage().'</error>');
}
}
foreach ($driver->getTags() as $tag => $identifier) {
$msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $tag . '</comment>)';
if ($verbose) {
$this->io->write($msg);
$this->io->writeError($msg);
} else {
$this->io->overwrite($msg, false);
$this->io->overwriteError($msg, false);
}
// strip the release- prefix from tags if present
@ -144,7 +144,7 @@ class VcsRepository extends ArrayRepository
if (!$parsedTag = $this->validateTag($tag)) {
if ($verbose) {
$this->io->write('<warning>Skipped tag '.$tag.', invalid tag name</warning>');
$this->io->writeError('<warning>Skipped tag '.$tag.', invalid tag name</warning>');
}
continue;
}
@ -152,7 +152,7 @@ class VcsRepository extends ArrayRepository
try {
if (!$data = $driver->getComposerInformation($identifier)) {
if ($verbose) {
$this->io->write('<warning>Skipped tag '.$tag.', no composer file</warning>');
$this->io->writeError('<warning>Skipped tag '.$tag.', no composer file</warning>');
}
continue;
}
@ -173,39 +173,39 @@ class VcsRepository extends ArrayRepository
// broken package, version doesn't match tag
if ($data['version_normalized'] !== $parsedTag) {
if ($verbose) {
$this->io->write('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
$this->io->writeError('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
}
continue;
}
if ($verbose) {
$this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')');
$this->io->writeError('Importing tag '.$tag.' ('.$data['version_normalized'].')');
}
$this->addPackage($this->loader->load($this->preProcess($driver, $data, $identifier)));
} catch (\Exception $e) {
if ($verbose) {
$this->io->write('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()).'</warning>');
$this->io->writeError('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()).'</warning>');
}
continue;
}
}
if (!$verbose) {
$this->io->overwrite('', false);
$this->io->overwriteError('', false);
}
foreach ($driver->getBranches() as $branch => $identifier) {
$msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)';
if ($verbose) {
$this->io->write($msg);
$this->io->writeError($msg);
} else {
$this->io->overwrite($msg, false);
$this->io->overwriteError($msg, false);
}
if (!$parsedBranch = $this->validateBranch($branch)) {
if ($verbose) {
$this->io->write('<warning>Skipped branch '.$branch.', invalid name</warning>');
$this->io->writeError('<warning>Skipped branch '.$branch.', invalid name</warning>');
}
continue;
}
@ -213,7 +213,7 @@ class VcsRepository extends ArrayRepository
try {
if (!$data = $driver->getComposerInformation($identifier)) {
if ($verbose) {
$this->io->write('<warning>Skipped branch '.$branch.', no composer file</warning>');
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file</warning>');
}
continue;
}
@ -230,7 +230,7 @@ class VcsRepository extends ArrayRepository
}
if ($verbose) {
$this->io->write('Importing branch '.$branch.' ('.$data['version'].')');
$this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')');
}
$packageData = $this->preProcess($driver, $data, $identifier);
@ -241,23 +241,23 @@ class VcsRepository extends ArrayRepository
$this->addPackage($package);
} catch (TransportException $e) {
if ($verbose) {
$this->io->write('<warning>Skipped branch '.$branch.', no composer file was found</warning>');
$this->io->writeError('<warning>Skipped branch '.$branch.', no composer file was found</warning>');
}
continue;
} catch (\Exception $e) {
if (!$verbose) {
$this->io->write('');
$this->io->writeError('');
}
$this->branchErrorOccurred = true;
$this->io->write('<error>Skipped branch '.$branch.', '.$e->getMessage().'</error>');
$this->io->write('');
$this->io->writeError('<error>Skipped branch '.$branch.', '.$e->getMessage().'</error>');
$this->io->writeError('');
continue;
}
}
$driver->cleanup();
if (!$verbose) {
$this->io->overwrite('', false);
$this->io->overwriteError('', false);
}
if (!$this->getPackages()) {

View File

@ -15,7 +15,7 @@ namespace Composer\Script;
/**
* The Command Event.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
* @deprecated use Composer\Script\Event instead
*/
class CommandEvent extends Event
{

View File

@ -12,44 +12,13 @@
namespace Composer\Script;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\Installer\PackageEvent as BasePackageEvent;
/**
* The Package Event.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @deprecated Use Composer\Installer\PackageEvent instead
*/
class PackageEvent extends Event
class PackageEvent extends BasePackageEvent
{
/**
* @var OperationInterface The package instance
*/
private $operation;
/**
* Constructor.
*
* @param string $name The event name
* @param Composer $composer The composer object
* @param IOInterface $io The IOInterface object
* @param boolean $devMode Whether or not we are in dev mode
* @param OperationInterface $operation The operation object
*/
public function __construct($name, Composer $composer, IOInterface $io, $devMode, OperationInterface $operation)
{
parent::__construct($name, $composer, $io, $devMode);
$this->operation = $operation;
}
/**
* Returns the package instance.
*
* @return OperationInterface
*/
public function getOperation()
{
return $this->operation;
}
}

View File

@ -74,59 +74,7 @@ class ScriptEvents
*/
const POST_STATUS_CMD = 'post-status-cmd';
/**
* The PRE_PACKAGE_INSTALL event occurs before a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_INSTALL = 'pre-package-install';
/**
* The POST_PACKAGE_INSTALL event occurs after a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_INSTALL = 'post-package-install';
/**
* The PRE_PACKAGE_UPDATE event occurs before a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UPDATE = 'pre-package-update';
/**
* The POST_PACKAGE_UPDATE event occurs after a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UPDATE = 'post-package-update';
/**
* The PRE_PACKAGE_UNINSTALL event occurs before a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall';
/**
* The POST_PACKAGE_UNINSTALL event occurs after a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UNINSTALL = 'post-package-uninstall';
/** Deprecated constants below */
/**
* The PRE_AUTOLOAD_DUMP event occurs before the autoload file is generated.
@ -182,4 +130,64 @@ class ScriptEvents
* @var string
*/
const POST_ARCHIVE_CMD = 'post-archive-cmd';
/**
* The PRE_PACKAGE_INSTALL event occurs before a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_INSTALL instead.
* @var string
*/
const PRE_PACKAGE_INSTALL = 'pre-package-install';
/**
* The POST_PACKAGE_INSTALL event occurs after a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_INSTALL instead.
* @var string
*/
const POST_PACKAGE_INSTALL = 'post-package-install';
/**
* The PRE_PACKAGE_UPDATE event occurs before a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_UPDATE instead.
* @var string
*/
const PRE_PACKAGE_UPDATE = 'pre-package-update';
/**
* The POST_PACKAGE_UPDATE event occurs after a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_UPDATE instead.
* @var string
*/
const POST_PACKAGE_UPDATE = 'post-package-update';
/**
* The PRE_PACKAGE_UNINSTALL event occurs before a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_UNINSTALL instead.
* @var string
*/
const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall';
/**
* The POST_PACKAGE_UNINSTALL event occurs after a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_UNINSTALL instead.
* @var string
*/
const POST_PACKAGE_UNINSTALL = 'post-package-uninstall';
}

View File

@ -52,12 +52,13 @@ class Git
}
}
$protocols = $this->config->get('github-protocols');
if (!is_array($protocols)) {
throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols));
}
// public github, autoswitch protocols
if (preg_match('{^(?:https?|git)://'.self::getGitHubDomainsRegex($this->config).'/(.*)}', $url, $match)) {
$protocols = $this->config->get('github-protocols');
if (!is_array($protocols)) {
throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols));
}
$messages = array();
foreach ($protocols as $protocol) {
if ('ssh' === $protocol) {
@ -79,8 +80,11 @@ class Git
$this->throwException('Failed to clone ' . self::sanitizeUrl($url) .' via '.implode(', ', $protocols).' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url);
}
// if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https
$bypassSshForGitHub = preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url) && !in_array('ssh', $protocols, true);
$command = call_user_func($commandCallable, $url);
if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) {
if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) {
// private github repository without git access, try https with auth
if (preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url, $match)) {
if (!$this->io->hasAuthentication($match[1])) {
@ -122,7 +126,7 @@ class Git
}
}
$this->io->write(' Authentication required (<info>'.parse_url($url, PHP_URL_HOST).'</info>):');
$this->io->writeError(' Authentication required (<info>'.parse_url($url, PHP_URL_HOST).'</info>):');
$auth = array(
'username' => $this->io->ask(' Username: ', $defaultUsername),
'password' => $this->io->askAndHideAnswer(' Password: '),
@ -160,18 +164,22 @@ class Git
// added in git 1.7.1, prevents prompting the user for username/password
if (getenv('GIT_ASKPASS') !== 'echo') {
putenv('GIT_ASKPASS=echo');
unset($_SERVER['GIT_ASKPASS']);
}
// clean up rogue git env vars in case this is running in a git hook
if (getenv('GIT_DIR')) {
putenv('GIT_DIR');
unset($_SERVER['GIT_DIR']);
}
if (getenv('GIT_WORK_TREE')) {
putenv('GIT_WORK_TREE');
unset($_SERVER['GIT_WORK_TREE']);
}
// clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
putenv("DYLD_LIBRARY_PATH");
unset($_SERVER['DYLD_LIBRARY_PATH']);
}
public static function getGitHubDomainsRegex(Config $config)

View File

@ -77,11 +77,11 @@ class GitHub
public function authorizeOAuthInteractively($originUrl, $message = null)
{
if ($message) {
$this->io->write($message);
$this->io->writeError($message);
}
$this->io->write(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName()));
$this->io->write('To revoke access to this token you can visit https://github.com/settings/applications');
$this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName()));
$this->io->writeError('To revoke access to this token you can visit https://github.com/settings/applications');
$otp = null;
$attemptCounter = 0;
@ -105,13 +105,13 @@ class GitHub
}
if (401 === $e->getCode()) {
$this->io->write('Bad credentials.');
$this->io->writeError('Bad credentials.');
} else {
$this->io->write('Maximum number of login attempts exceeded. Please try again later.');
$this->io->writeError('Maximum number of login attempts exceeded. Please try again later.');
}
$this->io->write('You can also manually create a personal token at https://github.com/settings/applications');
$this->io->write('Add it using "composer config github-oauth.github.com <token>"');
$this->io->writeError('You can also manually create a personal token at https://github.com/settings/applications');
$this->io->writeError('Add it using "composer config github-oauth.github.com <token>"');
continue;
}
@ -166,7 +166,7 @@ class GitHub
)
));
$this->io->write('Token successfully created');
$this->io->writeError('Token successfully created');
return JsonFile::parseJson($json);
}
@ -184,14 +184,14 @@ class GitHub
list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1)));
if ('required' === $required) {
$this->io->write('Two-factor Authentication');
$this->io->writeError('Two-factor Authentication');
if ('app' === $method) {
$this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.');
$this->io->writeError('Open the two-factor authentication app on your device to view your authentication code and verify your identity.');
}
if ('sms' === $method) {
$this->io->write('You have been sent an SMS message with an authentication code to verify your identity.');
$this->io->writeError('You have been sent an SMS message with an authentication code to verify your identity.');
}
return $this->io->ask('Authentication Code: ');

View File

@ -45,7 +45,7 @@ class ProcessExecutor
{
if ($this->io && $this->io->isDebug()) {
$safeCommand = preg_replace('{(://[^:/\s]+:)[^@\s/]+}i', '$1****', $command);
$this->io->write('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand);
$this->io->writeError('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand);
}
// make sure that null translate to the proper directory in case the dir is a symlink
@ -56,7 +56,7 @@ class ProcessExecutor
$this->captureOutput = count(func_get_args()) > 1;
$this->errorOutput = null;
$process = new Process($command, $cwd, null, null, static::getTimeout());
$process = new Process($command, $cwd, array_replace($_ENV, $_SERVER, array('LANGUAGE' => 'C')), null, static::getTimeout());
$callback = is_callable($output) ? $output : array($this, 'outputHandler');
$process->run($callback);

View File

@ -146,7 +146,7 @@ class RemoteFilesystem
$options = $this->getOptionsForUrl($originUrl, $additionalOptions);
if ($this->io->isDebug()) {
$this->io->write((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
$this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
}
if (isset($options['github-token'])) {
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
@ -158,7 +158,7 @@ class RemoteFilesystem
$ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
if ($this->progress) {
$this->io->write(" Downloading: <comment>connection...</comment>", false);
$this->io->writeError(" Downloading: <comment>Connecting...</comment>", false);
}
$errorMessage = '';
@ -228,7 +228,7 @@ class RemoteFilesystem
}
if ($this->progress && !$this->retry) {
$this->io->overwrite(" Downloading: <comment>100%</comment>");
$this->io->overwriteError(" Downloading: <comment>100%</comment>");
}
// handle copy command if download was successful
@ -327,9 +327,9 @@ class RemoteFilesystem
$progression = round($bytesTransferred / $this->bytesMax * 100);
}
if ((0 === $progression % 5) && $progression !== $this->lastProgress) {
if ((0 === $progression % 5) && 100 !== $progression && $progression !== $this->lastProgress) {
$this->lastProgress = $progression;
$this->io->overwrite(" Downloading: <comment>$progression%</comment>", false);
$this->io->overwriteError(" Downloading: <comment>$progression%</comment>", false);
}
}
break;
@ -371,7 +371,7 @@ class RemoteFilesystem
throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
}
$this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
$this->io->overwriteError(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
$username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: ');
$this->io->setAuthentication($this->originUrl, $username, $password);

View File

@ -81,6 +81,7 @@ class Svn
{
// clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
putenv("DYLD_LIBRARY_PATH");
unset($_SERVER['DYLD_LIBRARY_PATH']);
}
/**
@ -111,7 +112,7 @@ class Svn
}
$output .= $buffer;
if ($verbose) {
$io->write($buffer, false);
$io->writeError($buffer, false);
}
};
$status = $this->process->execute($svnCommand, $handler, $cwd);
@ -169,7 +170,7 @@ class Svn
);
}
$this->io->write("The Subversion server ({$this->url}) requested credentials:");
$this->io->writeError("The Subversion server ({$this->url}) requested credentials:");
$this->hasAuth = true;
$this->credentials['username'] = $this->io->ask("Username: ");

View File

@ -42,7 +42,8 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase
}
if ($this->oldenv) {
$fs->removeDirectory(getenv('COMPOSER_HOME'));
putenv('COMPOSER_HOME='.$this->oldenv);
$_SERVER['COMPOSER_HOME'] = $this->oldenv;
putenv('COMPOSER_HOME='.$_SERVER['COMPOSER_HOME']);
$this->oldenv = null;
}
}
@ -86,10 +87,11 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase
$testData = $this->parseTestFile($testFile);
$this->oldenv = getenv('COMPOSER_HOME');
putenv('COMPOSER_HOME='.$this->testDir.'home');
$_SERVER['COMPOSER_HOME'] = $this->testDir.'home';
putenv('COMPOSER_HOME='.$_SERVER['COMPOSER_HOME']);
$cmd = 'php '.escapeshellarg(self::$pharPath).' --no-ansi '.$testData['RUN'];
$proc = new Process($cmd, __DIR__.'/Fixtures/functional');
$proc = new Process($cmd, __DIR__.'/Fixtures/functional', null, null, 300);
$exitcode = $proc->run();
if (isset($testData['EXPECT'])) {

View File

@ -29,7 +29,7 @@ class ApplicationTest extends TestCase
->will($this->returnValue('list'));
$outputMock->expects($this->once())
->method("writeln")
->method("write")
->with($this->equalTo(sprintf('<warning>Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF'])));
if (!defined('COMPOSER_DEV_WARNING_TIME')) {

View File

@ -106,6 +106,7 @@ class AutoloadGeneratorTest extends TestCase
$ret = $ret();
}
}
return $ret;
}));

View File

@ -74,6 +74,13 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php',
));
}
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) {
$data[] = array(__DIR__.'/Fixtures/hhvm3.3', array(
'FooEnum' => __DIR__.'/Fixtures/hhvm3.3/HackEnum.php',
'Foo\BarEnum' => __DIR__.'/Fixtures/hhvm3.3/NamespacedHackEnum.php',
'GenericsClass' => __DIR__.'/Fixtures/hhvm3.3/Generics.php',
));
}
return $data;
}
@ -128,7 +135,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
$msg = '';
$io->expects($this->once())
->method('write')
->method('writeError')
->will($this->returnCallback(function ($text) use (&$msg) {
$msg = $text;
}));

View File

@ -0,0 +1,4 @@
<?hh
class GenericsClass<Tk, Tv> {
}

View File

@ -0,0 +1,6 @@
<?hh
enum FooEnum: int {
HERP = 1;
DERP = 2;
}

View File

@ -0,0 +1,7 @@
<?hh
namespace Foo;
enum BarEnum: string {
HERP = 'DERP';
}

View File

@ -0,0 +1,6 @@
{
"name": "my-vend/my-app",
"license": "MIT",
"repositories": {
}
}

View File

@ -0,0 +1,10 @@
{
"name": "my-vend/my-app",
"license": "MIT",
"repositories": {
"example_tld": {
"type": "git",
"url": "example.tld"
}
}
}

Some files were not shown because too many files have changed in this diff Show More