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 language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
addons:
apt_packages:
- parallel
php: php:
- 5.3.3 - 5.3.3
- 5.3 - 5.3
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0
- hhvm - hhvm
- hhvm-nightly
matrix:
fast_finish: true
allow_failures:
- php: hhvm-nightly
before_script: before_script:
- sudo apt-get install parallel
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
- composer install --prefer-source - composer install
- bin/composer install --prefer-source - bin/composer install
- git config --global user.name travis-ci - git config --global user.name travis-ci
- git config --global user.email travis@example.com - 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')); $memoryLimit = trim(ini_get('memory_limit'));
// Increase memory_limit if it is lower than 512M // Increase memory_limit if it is lower than 1GB
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 512 * 1024 * 1024) { if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1024) {
@ini_set('memory_limit', '512M'); @ini_set('memory_limit', '1G');
} }
unset($memoryInBytes, $memoryLimit); unset($memoryInBytes, $memoryLimit);
} }

View File

@ -23,14 +23,14 @@
}, },
"require": { "require": {
"php": ">=5.3.2", "php": ">=5.3.2",
"justinrainbow/json-schema": "~1.3", "justinrainbow/json-schema": "~1.4",
"seld/jsonlint": "~1.0", "seld/jsonlint": "~1.0",
"symfony/console": "~2.3", "symfony/console": "~2.5",
"symfony/finder": "~2.2", "symfony/finder": "~2.2",
"symfony/process": "~2.1" "symfony/process": "~2.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "~4.5"
}, },
"suggest": { "suggest": {
"ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic", "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", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "2bc9cc8aa706b68d611d7058e4eb8de7", "hash": "8317ca3b690ea80633fe3fb2b0d440cb",
"packages": [ "packages": [
{ {
"name": "justinrainbow/json-schema", "name": "justinrainbow/json-schema",
"version": "1.3.7", "version": "1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/justinrainbow/json-schema.git", "url": "https://github.com/justinrainbow/json-schema.git",
"reference": "87b54b460febed69726c781ab67462084e97a105" "reference": "680d026082c3aa234b2d8617c50e9c73999913ba"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/87b54b460febed69726c781ab67462084e97a105", "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/680d026082c3aa234b2d8617c50e9c73999913ba",
"reference": "87b54b460febed69726c781ab67462084e97a105", "reference": "680d026082c3aa234b2d8617c50e9c73999913ba",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -70,20 +70,20 @@
"json", "json",
"schema" "schema"
], ],
"time": "2014-08-25 02:48:14" "time": "2015-03-23 20:38:38"
}, },
{ {
"name": "seld/jsonlint", "name": "seld/jsonlint",
"version": "1.3.0", "version": "1.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/jsonlint.git", "url": "https://github.com/Seldaek/jsonlint.git",
"reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35" "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/a7bc2ec9520ad15382292591b617c43bdb1fec35", "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/863ae85c6d3ef60ca49cb12bd051c4a0648c40c4",
"reference": "a7bc2ec9520ad15382292591b617c43bdb1fec35", "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -116,21 +116,21 @@
"parser", "parser",
"validator" "validator"
], ],
"time": "2014-09-05 15:36:20" "time": "2015-01-04 21:18:15"
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v2.6.1", "version": "v2.6.5",
"target-dir": "Symfony/Component/Console", "target-dir": "Symfony/Component/Console",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Console.git", "url": "https://github.com/symfony/Console.git",
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8" "reference": "53f86497ccd01677e22435cfb7262599450a90d1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/ef825fd9f809d275926547c9e57cbf14968793e8", "url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1",
"reference": "ef825fd9f809d275926547c9e57cbf14968793e8", "reference": "53f86497ccd01677e22435cfb7262599450a90d1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -139,6 +139,7 @@
"require-dev": { "require-dev": {
"psr/log": "~1.0", "psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1", "symfony/event-dispatcher": "~2.1",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.1" "symfony/process": "~2.1"
}, },
"suggest": { "suggest": {
@ -173,26 +174,29 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20" "time": "2015-03-13 17:37:22"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v2.6.1", "version": "v2.6.5",
"target-dir": "Symfony/Component/Finder", "target-dir": "Symfony/Component/Finder",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Finder.git", "url": "https://github.com/symfony/Finder.git",
"reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721" "reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721", "url": "https://api.github.com/repos/symfony/Finder/zipball/bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"reference": "0d3ef7f6ec55a7af5eca7914eaa0dacc04ccc721", "reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -220,26 +224,29 @@
], ],
"description": "Symfony Finder Component", "description": "Symfony Finder Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20" "time": "2015-03-12 10:28:44"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v2.6.1", "version": "v2.6.5",
"target-dir": "Symfony/Component/Process", "target-dir": "Symfony/Component/Process",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Process.git", "url": "https://github.com/symfony/Process.git",
"reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a" "reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/bf0c9bd625f13b0b0bbe39919225cf145dfb935a", "url": "https://api.github.com/repos/symfony/Process/zipball/4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"reference": "bf0c9bd625f13b0b0bbe39919225cf145dfb935a", "reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -267,7 +274,7 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20" "time": "2015-03-12 10:28:44"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -326,17 +333,125 @@
"time": "2014-10-13 12:58:55" "time": "2014-10-13 12:58:55"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpdocumentor/reflection-docblock",
"version": "2.0.14", "version": "2.0.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94" "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca158276c1200cc27f5409a5e338486bc0b4fc94", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94", "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": "" "shasum": ""
}, },
"require": { "require": {
@ -349,7 +464,7 @@
}, },
"require-dev": { "require-dev": {
"ext-xdebug": ">=2.1.4", "ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4.1" "phpunit/phpunit": "~4"
}, },
"suggest": { "suggest": {
"ext-dom": "*", "ext-dom": "*",
@ -368,9 +483,6 @@
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [ "license": [
"BSD-3-Clause" "BSD-3-Clause"
], ],
@ -388,7 +500,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2014-12-26 13:28:33" "time": "2015-01-24 10:06:35"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -525,16 +637,16 @@
}, },
{ {
"name": "phpunit/php-token-stream", "name": "phpunit/php-token-stream",
"version": "1.3.0", "version": "1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git", "url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876" "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db32c18eba00b121c145575fcbcd4d4d24e6db74",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876", "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -547,7 +659,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.3-dev" "dev-master": "1.4-dev"
} }
}, },
"autoload": { "autoload": {
@ -570,20 +682,20 @@
"keywords": [ "keywords": [
"tokenizer" "tokenizer"
], ],
"time": "2014-08-31 06:12:13" "time": "2015-01-17 09:51:32"
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "4.4.1", "version": "4.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a" "reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a5e49a86ce5e33b8d0657abe145057fc513543a", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5b578d3865a9128b9c209b011fda6539ec06e7a5",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a", "reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -593,15 +705,16 @@
"ext-reflection": "*", "ext-reflection": "*",
"ext-spl": "*", "ext-spl": "*",
"php": ">=5.3.3", "php": ">=5.3.3",
"phpspec/prophecy": "~1.3.1",
"phpunit/php-code-coverage": "~2.0", "phpunit/php-code-coverage": "~2.0",
"phpunit/php-file-iterator": "~1.3.2", "phpunit/php-file-iterator": "~1.3.2",
"phpunit/php-text-template": "~1.2", "phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "~1.0.2", "phpunit/php-timer": "~1.0.2",
"phpunit/phpunit-mock-objects": "~2.3", "phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.0", "sebastian/comparator": "~1.1",
"sebastian/diff": "~1.1", "sebastian/diff": "~1.1",
"sebastian/environment": "~1.1", "sebastian/environment": "~1.2",
"sebastian/exporter": "~1.0", "sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0", "sebastian/global-state": "~1.0",
"sebastian/version": "~1.0", "sebastian/version": "~1.0",
"symfony/yaml": "~2.0" "symfony/yaml": "~2.0"
@ -615,7 +728,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.4.x-dev" "dev-master": "4.5.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -641,7 +754,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2014-12-28 07:57:05" "time": "2015-02-05 15:51:19"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -700,25 +813,25 @@
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
"version": "1.1.0", "version": "1.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git", "url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a" "reference": "1dd8869519a225f7f2b9eb663e225298fade819e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a", "reference": "1dd8869519a225f7f2b9eb663e225298fade819e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3", "php": ">=5.3.3",
"sebastian/diff": "~1.1", "sebastian/diff": "~1.2",
"sebastian/exporter": "~1.0" "sebastian/exporter": "~1.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.1" "phpunit/phpunit": "~4.4"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -760,7 +873,7 @@
"compare", "compare",
"equality" "equality"
], ],
"time": "2014-11-16 21:32:38" "time": "2015-01-29 16:28:08"
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
@ -866,28 +979,29 @@
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "1.0.2", "version": "1.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0" "reference": "84839970d05254c73cde183a721c7af13aede943"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", "reference": "84839970d05254c73cde183a721c7af13aede943",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3",
"sebastian/recursion-context": "~1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "~4.4"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.0.x-dev" "dev-master": "1.2.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -927,7 +1041,7 @@
"export", "export",
"exporter" "exporter"
], ],
"time": "2014-09-10 00:51:36" "time": "2015-01-27 07:23:06"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@ -980,6 +1094,59 @@
], ],
"time": "2014-10-06 09:23:50" "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", "name": "sebastian/version",
"version": "1.0.4", "version": "1.0.4",
@ -1017,22 +1184,25 @@
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v2.6.1", "version": "v2.6.5",
"target-dir": "Symfony/Component/Yaml", "target-dir": "Symfony/Component/Yaml",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Yaml.git", "url": "https://github.com/symfony/Yaml.git",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20" "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20", "url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20", "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -1060,7 +1230,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20" "time": "2015-03-12 10:28:44"
} }
], ],
"aliases": [], "aliases": [],

View File

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

View File

@ -894,4 +894,36 @@ The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`,
Optional. 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 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 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 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. [Libraries](02-libraries.md) for more information.
Example assuming you patched monolog to fix a bug in the `bugfix` branch: 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 ### Packagist
The underlying application used by packagist is open source. This means that you 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 can technically install your own copy of packagist. However it is not a
quite straight-forward to do. However due to its size and complexity, for most supported use case and changes will happen without caring for third parties
small and medium sized companies willing to track a few packages will be better using the code.
off using Satis.
Packagist is a Symfony2 application, and it is [available on Packagist is a Symfony2 application, and it is [available on
GitHub](https://github.com/composer/packagist). It uses composer internally and 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 of all VCS packages, periodically re-crawls them, and exposes them as a composer
repository. repository.
To set your own copy, simply follow the instructions from the [packagist ### Toran Proxy
github repository](https://github.com/composer/packagist).
[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 ### Satis

View File

@ -6,9 +6,15 @@
# Toran Proxy # 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 # 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. > **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 ### Downloads
When GitHub or BitBucket repositories are mirrored on your local satis, the build process will include 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 ```json
{ {
"basic-auth": { "http-basic": {
"repo.example1.org": { "repo.example1.org": {
"username": "my-username1", "username": "my-username1",
"password": "my-secret-password1" "password": "my-secret-password1"

View File

@ -82,15 +82,7 @@ Furthermore plugins may implement the
event handlers automatically registered with the `EventDispatcher` when the event handlers automatically registered with the `EventDispatcher` when the
plugin is loaded. plugin is loaded.
The events available for plugins are: Plugin can subscribe to any of the available [script events](scripts.md#event-names).
* **COMMAND**, is called at the beginning of all commands that load plugins.
It provides you with access to the input and output objects of the program.
* **PRE_FILE_DOWNLOAD**, is triggered before files are downloaded and allows
you to manipulate the `RemoteFilesystem` object prior to downloading files
based on the URL to be downloaded.
> A plugin can also subscribe to [script events][7].
Example: Example:
@ -148,7 +140,7 @@ list of installed packages. Additionally all plugin packages installed in the
local project plugins are loaded. local project plugins are loaded.
> You may pass the `--no-plugins` option to composer commands to disable all > 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. > causes errors and you wish to update or uninstall it.
[1]: ../04-schema.md#type [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 [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 [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 [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: Composer fires the following named events during its execution process:
### Command Events
- **pre-install-cmd**: occurs before the `install` command is executed. - **pre-install-cmd**: occurs before the `install` command is executed.
- **post-install-cmd**: occurs after the `install` command is executed. - **post-install-cmd**: occurs after the `install` command is executed.
- **pre-update-cmd**: occurs before the `update` command is executed. - **pre-update-cmd**: occurs before the `update` command is executed.
- **post-update-cmd**: occurs after the `update` command is executed. - **post-update-cmd**: occurs after the `update` command is executed.
- **pre-status-cmd**: occurs before the `status` command is executed. - **pre-status-cmd**: occurs before the `status` command is executed.
- **post-status-cmd**: occurs after the `status` command is executed. - **post-status-cmd**: occurs after the `status` command is executed.
- **pre-dependencies-solving**: occurs before the dependencies are resolved. - **pre-archive-cmd**: occurs before the `archive` command is executed.
- **post-dependencies-solving**: occurs after the dependencies are resolved. - **post-archive-cmd**: occurs after the `archive` command is executed.
- **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-autoload-dump**: occurs before the autoloader is dumped, either - **pre-autoload-dump**: occurs before the autoloader is dumped, either
during `install`/`update`, or via the `dump-autoload` command. during `install`/`update`, or via the `dump-autoload` command.
- **post-autoload-dump**: occurs after the autoloader is dumped, either - **post-autoload-dump**: occurs after the autoloader is dumped, either
@ -42,8 +38,28 @@ Composer fires the following named events during its execution process:
installed, during the `create-project` command. installed, during the `create-project` command.
- **post-create-project-cmd**: occurs after the `create-project` command is - **post-create-project-cmd**: occurs after the `create-project` command is
executed. 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 > **Note:** Composer makes no assumptions about the state of your dependencies
> prior to `install` or `update`. Therefore, you should not specify scripts > 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; namespace MyVendor;
use Composer\Script\Event; use Composer\Script\Event;
use Composer\Installer\PackageEvent;
class MyClass class MyClass
{ {
@ -105,7 +122,7 @@ class MyClass
// do stuff // do stuff
} }
public static function postPackageInstall(Event $event) public static function postPackageInstall(PackageEvent $event)
{ {
$installedPackage = $event->getOperation()->getPackage(); $installedPackage = $event->getOperation()->getPackage();
// do stuff // do stuff
@ -118,14 +135,21 @@ class MyClass
} }
``` ```
When an event is fired, Composer's internal event handler receives a When an event is fired, your PHP callback receives as first argument an
`Composer\Script\Event` object, which is passed as the first argument to your `Composer\EventDispatcher\Event` object. This object has a `getName()` method
PHP callback. This `Event` object has getters for other contextual objects: that lets you retrieve event name.
- `getComposer()`: returns the current instance of `Composer\Composer` Depending on the script types (see list above) you will get various event
- `getName()`: returns the name of the event being fired as a string subclasses containing various getters with relevant data and associated
- `getIO()`: returns the current input/output stream which implements objects:
`Composer\IO\IOInterface` for writing to the console
- 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 ## 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 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 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 **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 by providing an [alias version](../articles/aliases.md) for your development

View File

@ -413,6 +413,13 @@
"format": "uri" "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"; $classmapFile .= ");\n";
if (!$suffix) { 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); file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
@ -393,7 +402,7 @@ EOF;
} }
if (!$filesCode) { if (!$filesCode) {
return FALSE; return false;
} }
return <<<EOF return <<<EOF

View File

@ -92,7 +92,7 @@ class ClassMapGenerator
if (!isset($map[$class])) { if (!isset($map[$class])) {
$map[$class] = $filePath; $map[$class] = $filePath;
} elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) { } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
$io->write( $io->writeError(
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'. '<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>' ' 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) 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 { try {
$contents = @php_strip_whitespace($path); $contents = @php_strip_whitespace($path);
@ -129,7 +132,7 @@ class ClassMapGenerator
} }
// return early if there is no chance of matching anything in this file // 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(); return array();
} }
@ -154,7 +157,7 @@ class ClassMapGenerator
preg_match_all('{ 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*[\{;] | \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); }ix', $contents, $matches);
@ -170,6 +173,12 @@ class ClassMapGenerator
if ($name[0] === ':') { if ($name[0] === ':') {
// This is an XHP class, https://github.com/facebook/xhp // This is an XHP class, https://github.com/facebook/xhp
$name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); $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, '\\'); $classes[] = ltrim($namespace . $name, '\\');
} }

View File

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

View File

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

View File

@ -97,7 +97,7 @@ EOT
$package = $this->getComposer()->getPackage(); $package = $this->getComposer()->getPackage();
} }
$io->write('<info>Creating the archive.</info>'); $io->writeError('<info>Creating the archive.</info>');
$archiveManager->archive($package, $format, $dest); $archiveManager->archive($package, $format, $dest);
return 0; return 0;
@ -105,14 +105,14 @@ EOT
protected function selectPackage(IOInterface $io, $packageName, $version = null) 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)) { if ($composer = $this->getComposer(false)) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository(); $localRepo = $composer->getRepositoryManager()->getLocalRepository();
$repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories())); $repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
} else { } else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO()); $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); $repos = new CompositeRepository($defaultRepos);
} }
@ -125,14 +125,14 @@ EOT
if (count($packages) > 1) { if (count($packages) > 1) {
$package = reset($packages); $package = reset($packages);
$io->write('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>'); $io->writeError('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
$io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.'); $io->writeError('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('<comment>Please use a more specific constraint to pick a different package.</comment>');
} elseif ($packages) { } elseif ($packages) {
$package = reset($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 { } 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; return false;
} }

View File

@ -51,21 +51,21 @@ EOT
foreach ($cachePaths as $key => $cachePath) { foreach ($cachePaths as $key => $cachePath) {
$cachePath = realpath($cachePath); $cachePath = realpath($cachePath);
if (!$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); $cache = new Cache($io, $cachePath);
if (!$cache->isEnabled()) { 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); $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; protected $config;
/** /**
* @var Composer\Json\JsonFile * @var JsonFile
*/ */
protected $configFile; protected $configFile;
/** /**
* @var Composer\Config\JsonConfigSource * @var JsonConfigSource
*/ */
protected $configSource; protected $configSource;
/**
* @var JsonFile
*/
protected $authConfigFile;
/**
* @var JsonConfigSource
*/
protected $authConfigSource;
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -65,6 +75,15 @@ class ConfigCommand extends Command
This command allows you to edit some basic composer settings in either the This command allows you to edit some basic composer settings in either the
local composer.json file or the global config.json file. 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: To edit the global config.json file:
<comment>%command.full_name% --global</comment> <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> <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. <info>--global</info> option.
To edit the file in an external editor: To edit the file in an external editor:
@ -230,55 +257,13 @@ EOT
$value = json_encode($value); $value = json_encode($value);
} }
$output->writeln($value); $this->getIO()->write($value);
return 0; return 0;
} }
$values = $input->getArgument('setting-value'); // what the user is trying to add/change $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); }; $booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); };
$booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; }; $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'); 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) { 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 { } 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); $this->updatePreferredOptions($config, $input, $preferSource, $preferDist);
if ($input->getOption('no-custom-installers')) { 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); $input->setOption('no-plugins', true);
} }
@ -145,11 +145,13 @@ EOT
} }
$composer = Factory::create($io, null, $disablePlugins); $composer = Factory::create($io, null, $disablePlugins);
$composer->getDownloadManager()->setOutputProgress(!$noProgress);
$fs = new Filesystem(); $fs = new Filesystem();
if ($noScripts === false) { if ($noScripts === false) {
// dispatch event // dispatch event
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages); $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
} }
$rootPackageConfig = $composer->getConfig(); $rootPackageConfig = $composer->getConfig();
@ -196,7 +198,7 @@ EOT
} }
} }
} catch (\Exception $e) { } 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; $hasVcs = false;
@ -217,7 +219,7 @@ EOT
if ($noScripts === false) { if ($noScripts === false) {
// dispatch event // dispatch event
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages); $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
} }
chdir($oldCwd); chdir($oldCwd);
@ -288,10 +290,10 @@ EOT
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); $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) { 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'))) { if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
@ -311,10 +313,11 @@ EOT
$installedFromVcs = 'source' === $package->getInstallationSource(); $installedFromVcs = 'source' === $package->getInstallationSource();
$io->write('<info>Created project in ' . $directory . '</info>'); $io->writeError('<info>Created project in ' . $directory . '</info>');
chdir($directory); chdir($directory);
putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion()); $_SERVER['COMPOSER_ROOT_VERSION'] = $package->getPrettyVersion();
putenv('COMPOSER_ROOT_VERSION='.$_SERVER['COMPOSER_ROOT_VERSION']);
return $installedFromVcs; return $installedFromVcs;
} }

View File

@ -96,9 +96,9 @@ EOT
if ($messages) { if ($messages) {
sort($messages); sort($messages);
$output->writeln($messages); $this->getIO()->write($messages);
} else { } 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); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$output->write('Checking composer.json: '); $this->getIO()->write('Checking composer.json: ', false);
$this->outputResult($output, $this->checkComposerSchema()); $this->outputResult($this->checkComposerSchema());
} }
if ($composer) { if ($composer) {
@ -72,37 +72,37 @@ EOT
$this->rfs = new RemoteFilesystem($this->getIO(), $config); $this->rfs = new RemoteFilesystem($this->getIO(), $config);
$this->process = new ProcessExecutor($this->getIO()); $this->process = new ProcessExecutor($this->getIO());
$output->write('Checking platform settings: '); $this->getIO()->write('Checking platform settings: ', false);
$this->outputResult($output, $this->checkPlatform()); $this->outputResult($this->checkPlatform());
$output->write('Checking git settings: '); $this->getIO()->write('Checking git settings: ', false);
$this->outputResult($output, $this->checkGit()); $this->outputResult($this->checkGit());
$output->write('Checking http connectivity: '); $this->getIO()->write('Checking http connectivity: ', false);
$this->outputResult($output, $this->checkHttp()); $this->outputResult($this->checkHttp());
$opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org')); $opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
if (!empty($opts['http']['proxy'])) { if (!empty($opts['http']['proxy'])) {
$output->write('Checking HTTP proxy: '); $this->getIO()->write('Checking HTTP proxy: ', false);
$this->outputResult($output, $this->checkHttpProxy()); $this->outputResult($this->checkHttpProxy());
$output->write('Checking HTTP proxy support for request_fulluri: '); $this->getIO()->write('Checking HTTP proxy support for request_fulluri: ', false);
$this->outputResult($output, $this->checkHttpProxyFullUriRequestParam()); $this->outputResult($this->checkHttpProxyFullUriRequestParam());
$output->write('Checking HTTPS proxy support for request_fulluri: '); $this->getIO()->write('Checking HTTPS proxy support for request_fulluri: ', false);
$this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam()); $this->outputResult($this->checkHttpsProxyFullUriRequestParam());
} }
if ($oauth = $config->get('github-oauth')) { if ($oauth = $config->get('github-oauth')) {
foreach ($oauth as $domain => $token) { foreach ($oauth as $domain => $token) {
$output->write('Checking '.$domain.' oauth access: '); $this->getIO()->write('Checking '.$domain.' oauth access: ', false);
$this->outputResult($output, $this->checkGithubOauth($domain, $token)); $this->outputResult($this->checkGithubOauth($domain, $token));
} }
} else { } else {
$output->write('Checking github.com rate limit: '); $this->getIO()->write('Checking github.com rate limit: ', false);
$rate = $this->getGithubRateLimit('github.com'); $rate = $this->getGithubRateLimit('github.com');
if (10 > $rate['remaining']) { if (10 > $rate['remaining']) {
$output->writeln('<warning>WARNING</warning>'); $this->getIO()->write('<warning>WARNING</warning>');
$output->writeln(sprintf( $this->getIO()->write(sprintf(
'<comment>Github has a rate limit on their API. ' '<comment>Github has a rate limit on their API. '
. 'You currently have <options=bold>%u</options=bold> ' . 'You currently have <options=bold>%u</options=bold> '
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL . 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
@ -112,15 +112,15 @@ EOT
$rate['limit'] $rate['limit']
)); ));
} else { } else {
$output->writeln('<info>OK</info>'); $this->getIO()->write('<info>OK</info>');
} }
} }
$output->write('Checking disk free space: '); $this->getIO()->write('Checking disk free space: ', false);
$this->outputResult($output, $this->checkDiskSpace($config)); $this->outputResult($this->checkDiskSpace($config));
$output->write('Checking composer version: '); $this->getIO()->write('Checking composer version: ', false);
$this->outputResult($output, $this->checkVersion()); $this->outputResult($this->checkVersion());
return $this->failures; return $this->failures;
} }
@ -308,17 +308,17 @@ EOT
return true; return true;
} }
private function outputResult(OutputInterface $output, $result) private function outputResult($result)
{ {
if (true === $result) { if (true === $result) {
$output->writeln('<info>OK</info>'); $this->getIO()->write('<info>OK</info>');
} else { } else {
$this->failures++; $this->failures++;
$output->writeln('<error>FAIL</error>'); $this->getIO()->write('<error>FAIL</error>');
if ($result instanceof \Exception) { if ($result instanceof \Exception) {
$output->writeln('['.get_class($result).'] '.$result->getMessage()); $this->getIO()->write('['.get_class($result).'] '.$result->getMessage());
} elseif ($result) { } 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.'; $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')) { if (!ini_get('allow_url_fopen')) {
$errors['allow_url_fopen'] = true; $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', '<')) { if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$errors['php'] = PHP_VERSION; $errors['php'] = PHP_VERSION;
} }
@ -356,19 +380,13 @@ EOT
} }
if (!extension_loaded('openssl')) { if (!extension_loaded('openssl')) {
$warnings['openssl'] = true; $errors['openssl'] = true;
} }
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) { if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
$warnings['apc_cli'] = true; $warnings['apc_cli'] = true;
} }
if (ini_get('xdebug.profiler_enabled')) {
$warnings['xdebug_profile'] = true;
} elseif (extension_loaded('xdebug')) {
$warnings['xdebug_loaded'] = true;
}
ob_start(); ob_start();
phpinfo(INFO_GENERAL); phpinfo(INFO_GENERAL);
$phpinfo = ob_get_clean(); $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)) { if (!empty($errors)) {
foreach ($errors as $error => $current) { foreach ($errors as $error => $current) {
switch ($error) { 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': 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; break;
case 'allow_url_fopen': 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 .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " allow_url_fopen = On"; $text .= " allow_url_fopen = On";
$displayIniMessage = true; $displayIniMessage = true;
break; 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'); $out($text, 'error');
} }
@ -425,11 +500,6 @@ EOT
$text .= " Recompile it without this flag if possible"; $text .= " Recompile it without this flag if possible";
break; 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': case 'php':
$text = "Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL; $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."; $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'); $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
if ($optimize) { if ($optimize) {
$output->writeln('<info>Generating optimized autoload files</info>'); $this->getIO()->writeError('<info>Generating optimized autoload files</info>');
} else { } else {
$output->writeln('<info>Generating autoload files</info>'); $this->getIO()->writeError('<info>Generating autoload files</info>');
} }
$generator = $composer->getAutoloadGenerator(); $generator = $composer->getAutoloadGenerator();

View File

@ -72,7 +72,7 @@ EOT
// change to global dir // change to global dir
$config = Factory::createConfig(); $config = Factory::createConfig();
chdir($config->get('home')); 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 // create new input without "global" command prefix
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1)); $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) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$repo = $this->initializeRepo(); $repos = $this->initializeRepos();
$return = 0; $return = 0;
foreach ($input->getArgument('packages') as $packageName) { 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); $package = $this->getPackage($repo, $packageName);
if (!$package instanceof CompletePackageInterface) { if (!$package instanceof CompletePackageInterface) {
$return = 1; $return = 1;
$output->writeln('<warning>Package '.$packageName.' not found</warning>'); $this->getIO()->writeError('<warning>Package '.$packageName.' not found</warning>');
continue; continue;
} }
@ -78,13 +84,13 @@ EOT
if (!filter_var($url, FILTER_VALIDATE_URL)) { if (!filter_var($url, FILTER_VALIDATE_URL)) {
$return = 1; $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; continue;
} }
if ($input->getOption('show')) { if ($input->getOption('show')) {
$output->writeln(sprintf('<info>%s</info>', $url)); $this->getIO()->write(sprintf('<info>%s</info>', $url));
} else { } else {
$this->openBrowser($url); $this->openBrowser($url);
} }
@ -139,26 +145,30 @@ EOT
} elseif (0 === $osx) { } elseif (0 === $osx) {
passthru('open ' . $url); passthru('open ' . $url);
} else { } 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); $composer = $this->getComposer(false);
if ($composer) { if ($composer) {
$repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); return array(
} else { $composer->getRepositoryManager()->getLocalRepository(),
$defaultRepos = Factory::createDefaultRepositories($this->getIO()); new CompositeRepository($composer->getRepositoryManager()->getRepositories())
$repo = new CompositeRepository($defaultRepos); );
} }
return $repo; $defaultRepos = Factory::createDefaultRepositories($this->getIO());
return array(new CompositeRepository($defaultRepos));
} }
} }

View File

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

View File

@ -65,18 +65,18 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
if ($args = $input->getArgument('packages')) { 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; return 1;
} }
if ($input->getOption('no-custom-installers')) { 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); $input->setOption('no-plugins', true);
} }
if ($input->getOption('dev')) { 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')); $composer = $this->getComposer(true, $input->getOption('no-plugins'));

View File

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

View File

@ -73,7 +73,7 @@ EOT
if (isset($composer[$type][$package])) { if (isset($composer[$type][$package])) {
$json->removeLink($type, $package); $json->removeLink($type, $package);
} elseif (isset($composer[$altType][$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'); $dialog = $this->getHelperSet()->get('dialog');
if ($this->getIO()->isInteractive()) { if ($this->getIO()->isInteractive()) {
if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) { if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) {
@ -81,7 +81,7 @@ EOT
} }
} }
} else { } 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(); $status = $install->run();
if ($status !== 0) { 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); file_put_contents($jsonFile->getPath(), $composerBackup);
} }

View File

@ -67,17 +67,17 @@ EOT
$newlyCreated = !file_exists($file); $newlyCreated = !file_exists($file);
if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { 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; return 1;
} }
if (!is_readable($file)) { if (!is_readable($file)) {
$output->writeln('<error>'.$file.' is not readable.</error>'); $this->getIO()->writeError('<error>'.$file.' is not readable.</error>');
return 1; return 1;
} }
if (!is_writable($file)) { if (!is_writable($file)) {
$output->writeln('<error>'.$file.' is not writable.</error>'); $this->getIO()->writeError('<error>'.$file.' is not writable.</error>');
return 1; return 1;
} }
@ -122,7 +122,7 @@ EOT
$json->write($composerDefinition); $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')) { if ($input->getOption('no-update')) {
return 0; return 0;
@ -154,10 +154,10 @@ EOT
$status = $install->run(); $status = $install->run();
if ($status !== 0) { if ($status !== 0) {
if ($newlyCreated) { if ($newlyCreated) {
$output->writeln("\n".'<error>Installation failed, deleting '.$file.'.</error>'); $this->getIO()->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
unlink($json->getPath()); unlink($json->getPath());
} else { } 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); file_put_contents($json->getPath(), $composerBackup);
} }
} }

View File

@ -27,7 +27,7 @@ class RunScriptCommand extends Command
/** /**
* @var array Array with command events * @var array Array with command events
*/ */
protected $commandEvents = array( protected $scriptEvents = array(
ScriptEvents::PRE_INSTALL_CMD, ScriptEvents::PRE_INSTALL_CMD,
ScriptEvents::POST_INSTALL_CMD, ScriptEvents::POST_INSTALL_CMD,
ScriptEvents::PRE_UPDATE_CMD, ScriptEvents::PRE_UPDATE_CMD,
@ -35,17 +35,11 @@ class RunScriptCommand extends Command
ScriptEvents::PRE_STATUS_CMD, ScriptEvents::PRE_STATUS_CMD,
ScriptEvents::POST_STATUS_CMD, ScriptEvents::POST_STATUS_CMD,
ScriptEvents::POST_ROOT_PACKAGE_INSTALL, ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
ScriptEvents::POST_CREATE_PROJECT_CMD ScriptEvents::POST_CREATE_PROJECT_CMD,
);
/**
* @var array Array with script events
*/
protected $scriptEvents = array(
ScriptEvents::PRE_ARCHIVE_CMD, ScriptEvents::PRE_ARCHIVE_CMD,
ScriptEvents::POST_ARCHIVE_CMD, ScriptEvents::POST_ARCHIVE_CMD,
ScriptEvents::PRE_AUTOLOAD_DUMP, ScriptEvents::PRE_AUTOLOAD_DUMP,
ScriptEvents::POST_AUTOLOAD_DUMP ScriptEvents::POST_AUTOLOAD_DUMP,
); );
protected function configure() protected function configure()
@ -54,10 +48,11 @@ class RunScriptCommand extends Command
->setName('run-script') ->setName('run-script')
->setDescription('Run the scripts defined in composer.json.') ->setDescription('Run the scripts defined in composer.json.')
->setDefinition(array( ->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 InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), 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('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List scripts.'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The <info>run-script</info> command runs scripts defined in composer.json: The <info>run-script</info> command runs scripts defined in composer.json:
@ -70,8 +65,14 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) 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'); $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)))) { if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $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 // add the bin dir to the PATH to make local binaries of deps usable in scripts
$binDir = $composer->getConfig()->get('bin-dir'); $binDir = $composer->getConfig()->get('bin-dir');
if (is_dir($binDir)) { 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'); $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); 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 // add the bin dir to the PATH to make local binaries of deps usable in scripts
$binDir = $composer->getConfig()->get('bin-dir'); $binDir = $composer->getConfig()->get('bin-dir');
if (is_dir($binDir)) { 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(); $args = $input->getArguments();

View File

@ -62,7 +62,7 @@ EOT
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else { } else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO()); $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; $installedRepo = $platformRepo;
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
} }
@ -78,7 +78,7 @@ EOT
$results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags); $results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
foreach ($results as $result) { 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; $updateVersion = $input->getArgument('version') ?: $latestVersion;
if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $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; return 1;
} }
if (Composer::VERSION === $updateVersion) { 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; return 0;
} }
@ -104,11 +104,11 @@ EOT
self::OLD_INSTALL_EXT 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"); $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')); $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
if (!file_exists($tempFilename)) { if (!file_exists($tempFilename)) {
$output->writeln('<error>The download of the new composer version failed for an unexpected reason</error>'); $this->getIO()->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
return 1; return 1;
} }
@ -120,22 +120,22 @@ EOT
$fs = new Filesystem; $fs = new Filesystem;
foreach ($finder as $file) { foreach ($finder as $file) {
$file = (string) $file; $file = (string) $file;
$output->writeln('<info>Removing: '.$file.'</info>'); $this->getIO()->writeError('<info>Removing: '.$file.'</info>');
$fs->remove($file); $fs->remove($file);
} }
} }
if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) { if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
$output->writeln('<error>The file is corrupted ('.$err->getMessage().').</error>'); $this->getIO()->writeError('<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>Please re-run the self-update command to try again.</error>');
return 1; return 1;
} }
if (file_exists($backupFile)) { 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 { } 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; $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)) { 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; return 1;
} }

View File

@ -84,7 +84,7 @@ EOT
} else { } else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO()); $defaultRepos = Factory::createDefaultRepositories($this->getIO());
$repos = new CompositeRepository($defaultRepos); $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) { } elseif ($composer) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository(); $localRepo = $composer->getRepositoryManager()->getLocalRepository();
@ -92,7 +92,7 @@ EOT
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else { } else {
$defaultRepos = Factory::createDefaultRepositories($this->getIO()); $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; $installedRepo = $platformRepo;
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
} }
@ -119,9 +119,9 @@ EOT
$this->printLinks($input, $output, $package, 'requires'); $this->printLinks($input, $output, $package, 'requires');
$this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)'); $this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
if ($package->getSuggests()) { if ($package->getSuggests()) {
$output->writeln("\n<info>suggests</info>"); $this->getIO()->write("\n<info>suggests</info>");
foreach ($package->getSuggests() as $suggested => $reason) { 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'); $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) { foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
if (isset($packages[$type])) { if (isset($packages[$type])) {
if ($tree) { if ($tree) {
$output->writeln($type); $this->getIO()->write($type);
} }
ksort($packages[$type]); ksort($packages[$type]);
@ -222,10 +222,10 @@ EOT
} else { } else {
$output->write($indent . $package); $output->write($indent . $package);
} }
$output->writeln(''); $this->getIO()->write('');
} }
if ($tree) { 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) protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
{ {
$output->writeln('<info>name</info> : ' . $package->getPrettyName()); $this->getIO()->write('<info>name</info> : ' . $package->getPrettyName());
$output->writeln('<info>descrip.</info> : ' . $package->getDescription()); $this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
$output->writeln('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array())); $this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
$this->printVersions($input, $output, $package, $versions, $installedRepo, $repos); $this->printVersions($input, $output, $package, $versions, $installedRepo, $repos);
$output->writeln('<info>type</info> : ' . $package->getType()); $this->getIO()->write('<info>type</info> : ' . $package->getType());
$output->writeln('<info>license</info> : ' . implode(', ', $package->getLicense())); $this->getIO()->write('<info>license</info> : ' . implode(', ', $package->getLicense()));
$output->writeln('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $this->getIO()->write('<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())); $this->getIO()->write('<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>names</info> : ' . implode(', ', $package->getNames()));
if ($package->isAbandoned()) { if ($package->isAbandoned()) {
$replacement = ($package->getReplacementPackage() !== null) $replacement = ($package->getReplacementPackage() !== null)
? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.' ? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
: null; : null;
$output->writeln( $this->getIO()->writeError(
sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement) sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement)
); );
} }
if ($package->getSupport()) { if ($package->getSupport()) {
$output->writeln("\n<info>support</info>"); $this->getIO()->write("\n<info>support</info>");
foreach ($package->getSupport() as $type => $value) { foreach ($package->getSupport() as $type => $value) {
$output->writeln('<comment>' . $type . '</comment> : '.$value); $this->getIO()->write('<comment>' . $type . '</comment> : '.$value);
} }
} }
if ($package->getAutoload()) { if ($package->getAutoload()) {
$output->writeln("\n<info>autoload</info>"); $this->getIO()->write("\n<info>autoload</info>");
foreach ($package->getAutoload() as $type => $autoloads) { foreach ($package->getAutoload() as $type => $autoloads) {
$output->writeln('<comment>' . $type . '</comment>'); $this->getIO()->write('<comment>' . $type . '</comment>');
if ($type === 'psr-0') { if ($type === 'psr-0') {
foreach ($autoloads as $name => $path) { 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') { } elseif ($type === 'psr-4') {
foreach ($autoloads as $name => $path) { 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') { } elseif ($type === 'classmap') {
$output->writeln(implode(', ', $autoloads)); $this->getIO()->write(implode(', ', $autoloads));
} }
} }
if ($package->getIncludePaths()) { if ($package->getIncludePaths()) {
$output->writeln('<comment>include-path</comment>'); $this->getIO()->write('<comment>include-path</comment>');
$output->writeln(implode(', ', $package->getIncludePaths())); $this->getIO()->write(implode(', ', $package->getIncludePaths()));
} }
} }
} }
@ -355,7 +355,7 @@ EOT
$versions = implode(', ', $versions); $versions = implode(', ', $versions);
$output->writeln('<info>versions</info> : ' . $versions); $this->getIO()->write('<info>versions</info> : ' . $versions);
} }
/** /**
@ -371,10 +371,10 @@ EOT
{ {
$title = $title ?: $linkType; $title = $title ?: $linkType;
if ($links = $package->{'get'.ucfirst($linkType)}()) { if ($links = $package->{'get'.ucfirst($linkType)}()) {
$output->writeln("\n<info>" . $title . "</info>"); $this->getIO()->write("\n<info>" . $title . "</info>");
foreach ($links as $link) { 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(); $im = $composer->getInstallationManager();
// Dispatch pre-status-command // Dispatch pre-status-command
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::PRE_STATUS_CMD, true); $composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
$errors = array(); $errors = array();
@ -76,9 +76,9 @@ EOT
// output errors/warnings // output errors/warnings
if (!$errors) { if (!$errors) {
$output->writeln('<info>No local changes</info>'); $this->getIO()->writeError('<info>No local changes</info>');
} else { } 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) { foreach ($errors as $path => $changes) {
@ -86,19 +86,19 @@ EOT
$indentedChanges = implode("\n", array_map(function ($line) { $indentedChanges = implode("\n", array_map(function ($line) {
return ' ' . ltrim($line); return ' ' . ltrim($line);
}, explode("\n", $changes))); }, explode("\n", $changes)));
$output->writeln('<info>'.$path.'</info>:'); $this->getIO()->write('<info>'.$path.'</info>:');
$output->writeln($indentedChanges); $this->getIO()->write($indentedChanges);
} else { } else {
$output->writeln($path); $this->getIO()->write($path);
} }
} }
if ($errors && !$input->getOption('verbose')) { 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 // Dispatch post-status-command
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_STATUS_CMD, true); $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true);
return $errors ? 1 : 0; return $errors ? 1 : 0;
} }

View File

@ -75,12 +75,12 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
if ($input->getOption('no-custom-installers')) { 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); $input->setOption('no-plugins', true);
} }
if ($input->getOption('dev')) { 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')); $composer = $this->getComposer(true, $input->getOption('no-plugins'));

View File

@ -37,6 +37,7 @@ class ValidateCommand extends Command
->setDescription('Validates a composer.json') ->setDescription('Validates a composer.json')
->setDefinition(array( ->setDefinition(array(
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'), 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') new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
@ -57,45 +58,53 @@ EOT
$file = $input->getArgument('file'); $file = $input->getArgument('file');
if (!file_exists($file)) { if (!file_exists($file)) {
$output->writeln('<error>' . $file . ' not found.</error>'); $this->getIO()->writeError('<error>' . $file . ' not found.</error>');
return 1; return 1;
} }
if (!is_readable($file)) { if (!is_readable($file)) {
$output->writeln('<error>' . $file . ' is not readable.</error>'); $this->getIO()->writeError('<error>' . $file . ' is not readable.</error>');
return 1; return 1;
} }
$validator = new ConfigValidator($this->getIO()); $validator = new ConfigValidator($this->getIO());
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL; $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
$checkPublish = !$input->getOption('no-check-publish');
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
// output errors/warnings // output errors/warnings
if (!$errors && !$publishErrors && !$warnings) { if (!$errors && !$publishErrors && !$warnings) {
$output->writeln('<info>' . $file . ' is valid</info>'); $this->getIO()->write('<info>' . $file . ' is valid</info>');
} elseif (!$errors && !$publishErrors) { } elseif (!$errors && !$publishErrors) {
$output->writeln('<info>' . $file . ' is valid, but with a few warnings</info>'); $this->getIO()->writeError('<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('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
} elseif (!$errors) { } elseif (!$errors) {
$output->writeln('<info>' . $file . ' is valid for simple usage with composer but has</info>'); $this->getIO()->writeError('<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>'); $this->getIO()->writeError('<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('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
} else { } 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( $messages = array(
'error' => array_merge($errors, $publishErrors), 'error' => $errors,
'warning' => $warnings, '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 ($messages as $style => $msgs) {
foreach ($msgs as $msg) { 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() $finder->files()
->ignoreVCS(true) ->ignoreVCS(true)
->name('*.php') ->name('*.php')
->name('LICENSE')
->exclude('Tests') ->exclude('Tests')
->exclude('tests')
->exclude('docs')
->in(__DIR__.'/../../vendor/symfony/') ->in(__DIR__.'/../../vendor/symfony/')
->in(__DIR__.'/../../vendor/seld/jsonlint/src/') ->in(__DIR__.'/../../vendor/seld/jsonlint/')
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/src/') ->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
; ;
foreach ($finder as $file) { foreach ($finder as $file) {

View File

@ -15,6 +15,7 @@ namespace Composer\Console;
use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatter;
@ -91,7 +92,7 @@ class Application extends BaseApplication
$this->io = new ConsoleIO($input, $output, $this->getHelperSet()); $this->io = new ConsoleIO($input, $output, $this->getHelperSet());
if (version_compare(PHP_VERSION, '5.3.2', '<')) { 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')) { if (defined('COMPOSER_DEV_WARNING_TIME')) {
@ -104,7 +105,7 @@ class Application extends BaseApplication
} }
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
if (time() > COMPOSER_DEV_WARNING_TIME) { 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)) { if ($newWorkDir = $this->getNewWorkingDir($input)) {
$oldWorkingDir = getcwd(); $oldWorkingDir = getcwd();
chdir($newWorkDir); chdir($newWorkDir);
if ($output->getVerbosity() >= 4) { if ($this->getIO()->isDebug() >= 4) {
$output->writeln('Changed CWD to ' . getcwd()); $this->getIO()->writeError('Changed CWD to ' . getcwd());
} }
} }
@ -129,7 +130,7 @@ class Application extends BaseApplication
foreach ($composer['scripts'] as $script => $dummy) { foreach ($composer['scripts'] as $script => $dummy) {
if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
if ($this->has($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 { } else {
$this->add(new Command\ScriptAliasCommand($script)); $this->add(new Command\ScriptAliasCommand($script));
} }
@ -150,7 +151,7 @@ class Application extends BaseApplication
} }
if (isset($startTime)) { 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; 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 = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|| (($df = @disk_free_space($dir = sys_get_temp_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) { } catch (\Exception $e) {
} }
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) { 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>'); $this->getIO()->writeError('<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>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')) { 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>'); $this->getIO()->writeError('<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>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); $this->composer = Factory::create($this->io, null, $disablePlugins);
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {
if ($required) { if ($required) {
$this->io->write($e->getMessage()); $this->io->writeError($e->getMessage());
exit(1); exit(1);
} }
} catch (JsonValidationException $e) { } catch (JsonValidationException $e) {
@ -323,7 +328,6 @@ class Application extends BaseApplication
protected function getDefaultHelperSet() protected function getDefaultHelperSet()
{ {
$helperSet = parent::getDefaultHelperSet(); $helperSet = parent::getDefaultHelperSet();
$helperSet->set(new DialogHelper()); $helperSet->set(new DialogHelper());
return $helperSet; 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->stabilityFlags = $stabilityFlags;
$this->filterRequires = $filterRequires; $this->filterRequires = $filterRequires;
foreach ($filterRequires as $name => $constraint) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
unset($this->filterRequires[$name]);
}
}
} }
public function setWhitelist($whitelist) public function setWhitelist($whitelist)

View File

@ -87,6 +87,19 @@ class Problem
} }
if ($job && $job['cmd'] === 'install' && empty($packages)) { 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 // handle php extensions
if (0 === stripos($job['packageName'], 'ext-')) { if (0 === stripos($job['packageName'], 'ext-')) {
$ext = substr($job['packageName'], 4); $ext = substr($job['packageName'], 4);

View File

@ -217,7 +217,15 @@ class Rule
$targetName = $this->reasonData->getTarget(); $targetName = $this->reasonData->getTarget();
// handle php extensions // 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); $ext = substr($targetName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system'; $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 $branches = array();
protected $problems = array(); protected $problems = array();
protected $learnedPool = array(); protected $learnedPool = array();
protected $learnedWhy = array();
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) 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); $fileName = parent::download($package, $path);
if ($this->io->isVerbose()) { if ($this->io->isVerbose()) {
$this->io->write(' Extracting archive'); $this->io->writeError(' Extracting archive');
} }
try { try {
@ -77,7 +77,7 @@ abstract class ArchiveDownloader extends FileDownloader
// retry downloading if we have an invalid zip file // retry downloading if we have an invalid zip file
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) { if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
$this->io->write(' Invalid zip file, retrying...'); $this->io->writeError(' Invalid zip file, retrying...');
usleep(500000); usleep(500000);
continue; continue;
} }
@ -88,7 +88,7 @@ abstract class ArchiveDownloader extends FileDownloader
break; break;
} }
$this->io->write(''); $this->io->writeError('');
} }
/** /**

View File

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

View File

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

View File

@ -45,7 +45,7 @@ class GitDownloader extends VcsDownloader
$ref = $package->getSourceReference(); $ref = $package->getSourceReference();
$flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : ''; $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'; $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) { $commandCallable = function ($url) use ($ref, $path, $command) {
return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref)); return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref));
@ -78,7 +78,7 @@ class GitDownloader extends VcsDownloader
} }
$ref = $target->getSourceReference(); $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'; $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
$commandCallable = function ($url) use ($command) { $commandCallable = function ($url) use ($command) {
@ -143,10 +143,10 @@ class GitDownloader extends VcsDownloader
$changes = array_map(function ($elem) { $changes = array_map(function ($elem) {
return ' '.$elem; return ' '.$elem;
}, preg_split('{\s*\r?\n\s*}', $changes)); }, preg_split('{\s*\r?\n\s*}', $changes));
$this->io->write(' <error>The package has modified files:</error>'); $this->io->writeError(' <error>The package has modified files:</error>');
$this->io->write(array_slice($changes, 0, 10)); $this->io->writeError(array_slice($changes, 0, 10));
if (count($changes) > 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) { while (true) {
@ -167,21 +167,21 @@ class GitDownloader extends VcsDownloader
throw new \RuntimeException('Update aborted'); throw new \RuntimeException('Update aborted');
case 'v': case 'v':
$this->io->write($changes); $this->io->writeError($changes);
break; break;
case '?': case '?':
default: default:
help: help:
$this->io->write(array( $this->io->writeError(array(
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'), ' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up', ' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
' v - view modified files', ' v - view modified files',
)); ));
if ($update) { 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; break;
} }
} }
@ -195,7 +195,7 @@ class GitDownloader extends VcsDownloader
$path = $this->normalizePath($path); $path = $this->normalizePath($path);
if ($this->hasStashedChanges) { if ($this->hasStashedChanges) {
$this->hasStashedChanges = false; $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)) { if (0 !== $this->process->execute('git stash pop', $output, $path)) {
throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput()); 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") // reference was not found (prints "fatal: reference is not a tree: $ref")
if (false !== strpos($this->process->getErrorOutput(), $reference)) { 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()); 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); $url = ProcessExecutor::escape($url);
$ref = ProcessExecutor::escape($package->getSourceReference()); $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)); $command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
if (0 !== $this->process->execute($command, $ignoredOutput)) { if (0 !== $this->process->execute($command, $ignoredOutput)) {
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
@ -45,7 +45,7 @@ class HgDownloader extends VcsDownloader
{ {
$url = ProcessExecutor::escape($url); $url = ProcessExecutor::escape($url);
$ref = ProcessExecutor::escape($target->getSourceReference()); $ref = ProcessExecutor::escape($target->getSourceReference());
$this->io->write(" Updating to ".$target->getSourceReference()); $this->io->writeError(" Updating to ".$target->getSourceReference());
if (!is_dir($path.'/.hg')) { if (!is_dir($path.'/.hg')) {
throw new \RuntimeException('The .hg directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information'); 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(); $ref = $package->getSourceReference();
$label = $this->getLabelFromSourceReference($ref); $label = $this->getLabelFromSourceReference($ref);
$this->io->write(' Cloning ' . $ref); $this->io->writeError(' Cloning ' . $ref);
$this->initPerforce($package, $path, $url); $this->initPerforce($package, $path, $url);
$this->perforce->setStream($ref); $this->perforce->setStream($ref);
$this->perforce->p4Login($this->io); $this->perforce->p4Login($this->io);
@ -85,7 +85,7 @@ class PerforceDownloader extends VcsDownloader
*/ */
public function getLocalChanges(PackageInterface $package, $path) 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; return;
} }

View File

@ -29,7 +29,7 @@ class SvnDownloader extends VcsDownloader
SvnUtil::cleanEnv(); SvnUtil::cleanEnv();
$ref = $package->getSourceReference(); $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); $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); $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) { $changes = array_map(function ($elem) {
return ' '.$elem; return ' '.$elem;
}, preg_split('{\s*\r?\n\s*}', $changes)); }, preg_split('{\s*\r?\n\s*}', $changes));
$this->io->write(' <error>The package has modified files:</error>'); $this->io->writeError(' <error>The package has modified files:</error>');
$this->io->write(array_slice($changes, 0, 10)); $this->io->writeError(array_slice($changes, 0, 10));
if (count($changes) > 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) { while (true) {
@ -130,12 +130,12 @@ class SvnDownloader extends VcsDownloader
throw new \RuntimeException('Update aborted'); throw new \RuntimeException('Update aborted');
case 'v': case 'v':
$this->io->write($changes); $this->io->writeError($changes);
break; break;
case '?': case '?':
default: default:
$this->io->write(array( $this->io->writeError(array(
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'), ' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up', ' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
' v - view modified files', ' 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'); 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); $this->filesystem->emptyDirectory($path);
$urls = $package->getSourceUrls(); $urls = $package->getSourceUrls();
@ -67,9 +67,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
break; break;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->io->isDebug()) { 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)) { } elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL'); $this->io->writeError(' Failed, trying the next URL');
} }
if (!count($urls)) { if (!count($urls)) {
throw $e; 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); $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); $this->cleanChanges($initial, $path, true);
$urls = $target->getSourceUrls(); $urls = $target->getSourceUrls();
@ -117,9 +117,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
break; break;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->io->isDebug()) { 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)) { } elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL'); $this->io->writeError(' Failed, trying the next URL');
} else { } else {
// in case of failed update, try to reapply the changes before aborting // in case of failed update, try to reapply the changes before aborting
$this->reapplyChanges($path); $this->reapplyChanges($path);
@ -146,12 +146,12 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
return ' ' . $line; return ' ' . $line;
}, explode("\n", $logs))); }, explode("\n", $logs)));
$this->io->write(' '.$message); $this->io->writeError(' '.$message);
$this->io->write($logs); $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) 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); $this->cleanChanges($package, $path, false);
if (!$this->filesystem->removeDirectory($path)) { if (!$this->filesystem->removeDirectory($path)) {
throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); 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\DependencyResolver\Operation\OperationInterface;
use Composer\Repository\CompositeRepository; use Composer\Repository\CompositeRepository;
use Composer\Script; use Composer\Script;
use Composer\Script\CommandEvent;
use Composer\Script\PackageEvent; use Composer\Script\PackageEvent;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
@ -95,36 +94,28 @@ class EventDispatcher
/** /**
* Dispatch a package event. * Dispatch a package event.
* *
* @param string $eventName The constant in ScriptEvents * @param string $eventName The constant in PackageEvents
* @param boolean $devMode Whether or not we are in dev mode * @param bool $devMode Whether or not we are in dev mode
* @param OperationInterface $operation The package being installed/updated/removed * @param PolicyInterface $policy The policy
* @return int return code of the executed script if any, for php scripts a false return * @param Pool $pool The pool
* value is changed to 1, anything else to 0 * @param CompositeRepository $installedRepo The installed repository
*/ * @param Request $request The request
public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation) * @param array $operations The list of operations
{ * @param OperationInterface $operation The package being installed/updated/removed
return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
}
/**
* Dispatch a command event.
* *
* @param string $eventName The constant in ScriptEvents * @return int return code of the executed script if any, for php scripts a false return
* @param boolean $devMode Whether or not we are in dev mode * value is changed to 1, anything else to 0
* @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
*/ */
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. * Dispatch a installer event.
* *
* @param string $eventName The constant in InstallerEvents * @param string $eventName The constant in InstallerEvents
* @param bool $devMode Whether or not we are in dev mode
* @param PolicyInterface $policy The policy * @param PolicyInterface $policy The policy
* @param Pool $pool The pool * @param Pool $pool The pool
* @param CompositeRepository $installedRepo The installed repository * @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 * @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 * 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); $methodName = substr($callable, strpos($callable, '::') + 2);
if (!class_exists($className)) { 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; continue;
} }
if (!is_callable($callable)) { 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; continue;
} }
@ -175,13 +166,13 @@ class EventDispatcher
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0; $return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
} catch (\Exception $e) { } catch (\Exception $e) {
$message = "Script %s handling the %s event terminated with an exception"; $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; throw $e;
} }
} else { } else {
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments())); $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments()));
if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { 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); throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
} }
@ -214,10 +205,6 @@ class EventDispatcher
*/ */
protected function checkListenerExpectedEvent($target, Event $event) protected function checkListenerExpectedEvent($target, Event $event)
{ {
if (!$event instanceof Script\Event) {
return $event;
}
try { try {
$reflected = new \ReflectionParameter($target, 0); $reflected = new \ReflectionParameter($target, 0);
} catch (\Exception $e) { } catch (\Exception $e) {
@ -232,8 +219,24 @@ class EventDispatcher
$expected = $typehint->getName(); $expected = $typehint->getName();
// BC support
if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') { 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; return $event;

View File

@ -113,10 +113,10 @@ class Factory
$config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir))); $config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir)));
// load global config // load global config
$file = new JsonFile($home.'/config.json'); $file = new JsonFile($config->get('home').'/config.json');
if ($file->exists()) { if ($file->exists()) {
if ($io && $io->isDebug()) { if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $file->getPath()); $io->writeError('Loading config file ' . $file->getPath());
} }
$config->merge($file->read()); $config->merge($file->read());
} }
@ -126,7 +126,7 @@ class Factory
$file = new JsonFile($config->get('home').'/auth.json'); $file = new JsonFile($config->get('home').'/auth.json');
if ($file->exists()) { if ($file->exists()) {
if ($io && $io->isDebug()) { if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $file->getPath()); $io->writeError('Loading config file ' . $file->getPath());
} }
$config->merge(array('config' => $file->read())); $config->merge(array('config' => $file->read()));
} }
@ -227,12 +227,12 @@ class Factory
$config->merge($localConfig); $config->merge($localConfig);
if (isset($composerFile)) { if (isset($composerFile)) {
if ($io && $io->isDebug()) { if ($io && $io->isDebug()) {
$io->write('Loading config file ' . $composerFile); $io->writeError('Loading config file ' . $composerFile);
} }
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
if ($localAuthFile->exists()) { if ($localAuthFile->exists()) {
if ($io && $io->isDebug()) { 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->merge(array('config' => $localAuthFile->read()));
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); $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); $composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
} catch (\Exception $e) { } catch (\Exception $e) {
if ($io->isDebug()) { 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 class BufferIO extends ConsoleIO
{ {
/** /**
* @param string $input * @param string $input
* @param int $verbosity * @param int $verbosity
* @param OutputFormatterInterface $formatter * @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 = new StringInput($input);
$input->setInteractive(false); $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())); parent::__construct($input, $output, new HelperSet(array()));
} }

View File

@ -13,6 +13,7 @@
namespace Composer\IO; namespace Composer\IO;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\ExecutableFinder;
@ -29,6 +30,7 @@ class ConsoleIO extends BaseIO
protected $output; protected $output;
protected $helperSet; protected $helperSet;
protected $lastMessage; protected $lastMessage;
protected $lastMessageErr;
private $startTime; private $startTime;
/** /**
@ -94,6 +96,24 @@ class ConsoleIO extends BaseIO
* {@inheritDoc} * {@inheritDoc}
*/ */
public function write($messages, $newline = true) 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) { if (null !== $this->startTime) {
$memoryUsage = memory_get_usage() / 1024 / 1024; $memoryUsage = memory_get_usage() / 1024 / 1024;
@ -102,6 +122,13 @@ class ConsoleIO extends BaseIO
return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message); return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message);
}, (array) $messages); }, (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->output->write($messages, $newline);
$this->lastMessage = join($newline ? "\n" : '', (array) $messages); $this->lastMessage = join($newline ? "\n" : '', (array) $messages);
} }
@ -111,12 +138,29 @@ class ConsoleIO extends BaseIO
*/ */
public function overwrite($messages, $newline = true, $size = null) public function overwrite($messages, $newline = true, $size = null)
{ {
if (!$this->output->isDecorated()) { $this->doOverwrite($messages, $newline, $size, false);
if (!$messages) { }
return;
}
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 // 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... // since overwrite is supposed to overwrite last message...
if (!isset($size)) { if (!isset($size)) {
// removing possible formatting of lastMessage with strip_tags // 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 // ...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 // write the new message
$this->write($messages, false); $this->doWrite($messages, false, $stderr);
$fill = $size - strlen(strip_tags($messages)); $fill = $size - strlen(strip_tags($messages));
if ($fill > 0) { if ($fill > 0) {
// whitespace whatever has left // whitespace whatever has left
$this->write(str_repeat(' ', $fill), false); $this->doWrite(str_repeat(' ', $fill), false, $stderr);
// move the cursor back // move the cursor back
$this->write(str_repeat("\x08", $fill), false); $this->doWrite(str_repeat("\x08", $fill), false, $stderr);
} }
if ($newline) { 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) 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) 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) 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 // use bash if it's present
if ($finder->find('bash') && $finder->find('stty')) { 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"')); $value = rtrim(shell_exec('bash -c "stty -echo; read -n0 discard; read -r mypassword; stty echo; echo $mypassword"'));
$this->write(''); $this->writeError('');
return $value; return $value;
} }
@ -208,9 +284,9 @@ class ConsoleIO extends BaseIO
$exe = $tmpExe; $exe = $tmpExe;
} }
$this->write($question, false); $this->writeError($question, false);
$value = rtrim(shell_exec($exe)); $value = rtrim(shell_exec($exe));
$this->write(''); $this->writeError('');
// clean up // clean up
if (isset($tmpExe)) { if (isset($tmpExe)) {
@ -230,11 +306,11 @@ class ConsoleIO extends BaseIO
} }
} }
if (isset($shell)) { if (isset($shell)) {
$this->write($question, false); $this->writeError($question, false);
$readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword'; $readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command)); $value = rtrim(shell_exec($command));
$this->write(''); $this->writeError('');
return $value; return $value;
} }

View File

@ -64,6 +64,14 @@ interface IOInterface
*/ */
public function write($messages, $newline = true); 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. * Overwrites a previous message to the output.
* *
@ -73,6 +81,15 @@ interface IOInterface
*/ */
public function overwrite($messages, $newline = true, $size = null); 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. * Asks a question to the user.
* *

View File

@ -66,6 +66,13 @@ class NullIO extends BaseIO
{ {
} }
/**
* {@inheritDoc}
*/
public function writeError($messages, $newline = true)
{
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -73,6 +80,13 @@ class NullIO extends BaseIO
{ {
} }
/**
* {@inheritDoc}
*/
public function overwriteError($messages, $newline = true, $size = 80)
{
}
/** /**
* {@inheritDoc} * {@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 // 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')); $devRepo = new InstalledFilesystemRepository(new JsonFile($this->config->get('vendor-dir').'/composer/installed_dev.json'));
if ($devRepo->getPackages()) { 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) { foreach ($devRepo->getPackages() as $package) {
if ($this->installationManager->isPackageInstalled($devRepo, $package)) { if ($this->installationManager->isPackageInstalled($devRepo, $package)) {
$this->installationManager->uninstall($devRepo, new UninstallOperation($package)); $this->installationManager->uninstall($devRepo, new UninstallOperation($package));
@ -191,7 +191,7 @@ class Installer
if ($this->runScripts) { if ($this->runScripts) {
// dispatch pre event // dispatch pre event
$eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; $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); $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' ? 'Use ' . $package->getReplacementPackage() . ' instead'
: 'No replacement was suggested'; : 'No replacement was suggested';
$this->io->write( $this->io->writeError(
sprintf( sprintf(
"<error>Package %s is abandoned, you should avoid using it. %s.</error>", "<error>Package %s is abandoned, you should avoid using it. %s.</error>",
$package->getPrettyName(), $package->getPrettyName(),
@ -288,10 +288,10 @@ class Installer
$request->install($link->getTarget(), $link->getConstraint()); $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); $solver = new Solver($policy, $pool, $installedRepo);
$ops = $solver->solve($request, $this->ignorePlatformReqs); $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) { foreach ($ops as $op) {
if ($op->getJobType() === 'uninstall') { if ($op->getJobType() === 'uninstall') {
$devPackages[] = $op->getPackage(); $devPackages[] = $op->getPackage();
@ -314,16 +314,16 @@ class Installer
$this->preferLowest $this->preferLowest
); );
if ($updatedLock) { if ($updatedLock) {
$this->io->write('<info>Writing lock file</info>'); $this->io->writeError('<info>Writing lock file</info>');
} }
} }
if ($this->dumpAutoloader) { if ($this->dumpAutoloader) {
// write autoloader // write autoloader
if ($this->optimizeAutoloader) { if ($this->optimizeAutoloader) {
$this->io->write('<info>Generating optimized autoload files</info>'); $this->io->writeError('<info>Generating optimized autoload files</info>');
} else { } else {
$this->io->write('<info>Generating autoload files</info>'); $this->io->writeError('<info>Generating autoload files</info>');
} }
$this->autoloadGenerator->setDevMode($this->devMode); $this->autoloadGenerator->setDevMode($this->devMode);
@ -333,7 +333,7 @@ class Installer
if ($this->runScripts) { if ($this->runScripts) {
// dispatch post event // dispatch post event
$eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; $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'); $vendorDir = $this->config->get('vendor-dir');
@ -374,11 +374,11 @@ class Installer
$this->package->getDevRequires() $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 // creating repository pool
$policy = $this->createPolicy(); $policy = $this->createPolicy();
$pool = $this->createPool($withDevReqs); $pool = $this->createPool($withDevReqs, $lockedRepository);
$pool->addRepository($installedRepo, $aliases); $pool->addRepository($installedRepo, $aliases);
if ($installFromLock) { if ($installFromLock) {
$pool->addRepository($lockedRepository, $aliases); $pool->addRepository($lockedRepository, $aliases);
@ -409,7 +409,7 @@ class Installer
} }
if ($this->update) { 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(); $request->updateAll();
@ -460,10 +460,10 @@ class Installer
} }
} }
} elseif ($installFromLock) { } 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()) { 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) { foreach ($lockedRepository->getPackages() as $package) {
@ -480,7 +480,7 @@ class Installer
$request->install($link->getTarget(), $link->getConstraint()); $request->install($link->getTarget(), $link->getConstraint());
} }
} else { } 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) { if ($withDevReqs) {
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); $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'); $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links');
// solve dependencies // 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); $solver = new Solver($policy, $pool, $installedRepo);
try { try {
$operations = $solver->solve($request, $this->ignorePlatformReqs); $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) { } catch (SolverProblemsException $e) {
$this->io->write('<error>Your requirements could not be resolved to an installable set of packages.</error>'); $this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>');
$this->io->write($e->getMessage()); $this->io->writeError($e->getMessage());
return max(1, $e->getCode()); return max(1, $e->getCode());
} }
@ -514,7 +514,7 @@ class Installer
// execute operations // execute operations
if (!$operations) { if (!$operations) {
$this->io->write('Nothing to install or update'); $this->io->writeError('Nothing to install or update');
} }
$operations = $this->movePluginsToFront($operations); $operations = $this->movePluginsToFront($operations);
@ -553,26 +553,26 @@ class Installer
&& $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference() && $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference()
) { ) {
if ($this->io->isDebug()) { if ($this->io->isDebug()) {
$this->io->write(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version'); $this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
$this->io->write(''); $this->io->writeError('');
} }
continue; continue;
} }
} }
$event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType()); $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType());
if (defined($event) && $this->runScripts) { 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 // output non-alias ops in dry run, output alias ops in debug verbosity
if ($this->dryRun && false === strpos($operation->getJobType(), 'Alias')) { if ($this->dryRun && false === strpos($operation->getJobType(), 'Alias')) {
$this->io->write(' - ' . $operation); $this->io->writeError(' - ' . $operation);
$this->io->write(''); $this->io->writeError('');
} elseif ($this->io->isDebug() && false !== strpos($operation->getJobType(), 'Alias')) { } elseif ($this->io->isDebug() && false !== strpos($operation->getJobType(), 'Alias')) {
$this->io->write(' - ' . $operation); $this->io->writeError(' - ' . $operation);
$this->io->write(''); $this->io->writeError('');
} }
$this->installationManager->execute($localRepo, $operation); $this->installationManager->execute($localRepo, $operation);
@ -583,20 +583,20 @@ class Installer
if ($reason instanceof Rule) { if ($reason instanceof Rule) {
switch ($reason->getReason()) { switch ($reason->getReason()) {
case Rule::RULE_JOB_INSTALL: case Rule::RULE_JOB_INSTALL:
$this->io->write(' REASON: Required by root: '.$reason->getPrettyString($pool)); $this->io->writeError(' REASON: Required by root: '.$reason->getPrettyString($pool));
$this->io->write(''); $this->io->writeError('');
break; break;
case Rule::RULE_PACKAGE_REQUIRES: case Rule::RULE_PACKAGE_REQUIRES:
$this->io->write(' REASON: '.$reason->getPrettyString($pool)); $this->io->writeError(' REASON: '.$reason->getPrettyString($pool));
$this->io->write(''); $this->io->writeError('');
break; break;
} }
} }
} }
$event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType()); $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());
if (defined($event) && $this->runScripts) { 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) { if (!$this->dryRun) {
@ -671,27 +671,39 @@ class Installer
return array_merge($uninstOps, $operations); return array_merge($uninstOps, $operations);
} }
private function createPool($withDevReqs) private function createPool($withDevReqs, RepositoryInterface $lockedRepository = null)
{ {
$minimumStability = $this->package->getMinimumStability(); if (!$this->update && $this->locker->isLocked()) { // install from lock
$stabilityFlags = $this->package->getStabilityFlags();
if (!$this->update && $this->locker->isLocked()) {
$minimumStability = $this->locker->getMinimumStability(); $minimumStability = $this->locker->getMinimumStability();
$stabilityFlags = $this->locker->getStabilityFlags(); $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(); $rootConstraints = array();
foreach ($requires as $req => $constraint) { foreach ($requires as $req => $constraint) {
// skip platform requirements from the root package to avoid filtering out existing platform packages // skip platform requirements from the root package to avoid filtering out existing platform packages
if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
continue; continue;
} }
$rootConstraints[$req] = $constraint->getConstraint(); if ($constraint instanceof Link) {
$rootConstraints[$req] = $constraint->getConstraint();
} else {
$rootConstraints[$req] = $constraint;
}
} }
return new Pool($minimumStability, $stabilityFlags, $rootConstraints); return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
@ -989,7 +1001,7 @@ class Installer
} }
if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock'))) { 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) { foreach ($depPackages as $depPackage) {
@ -1166,7 +1178,6 @@ class Installer
return $this; return $this;
} }
/** /**
* set whether to run autoloader or not * set whether to run autoloader or not
* *
@ -1180,7 +1191,6 @@ class Installer
return $this; return $this;
} }
/** /**
* set whether to run scripts or not * set whether to run scripts or not
* *

View File

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

View File

@ -197,7 +197,7 @@ class LibraryInstaller implements InstallerInterface
foreach ($binaries as $bin) { foreach ($binaries as $bin) {
$binPath = $this->getInstallPath($package).'/'.$bin; $binPath = $this->getInstallPath($package).'/'.$bin;
if (!file_exists($binPath)) { 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; continue;
} }
@ -216,7 +216,7 @@ class LibraryInstaller implements InstallerInterface
// is a fresh install of the vendor. // is a fresh install of the vendor.
@chmod($link, 0777 & ~umask()); @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; continue;
} }
if (defined('PHP_WINDOWS_VERSION_BUILD')) { if (defined('PHP_WINDOWS_VERSION_BUILD')) {
@ -226,7 +226,7 @@ class LibraryInstaller implements InstallerInterface
@chmod($link, 0777 & ~umask()); @chmod($link, 0777 & ~umask());
$link .= '.bat'; $link .= '.bat';
if (file_exists($link)) { 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)) { 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); $pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin', 'data' => '/data'), $vars);
if ($this->io->isVerbose()) { if ($this->io->isVerbose()) {
$this->io->write(' Cleaning up'); $this->io->writeError(' Cleaning up');
} }
$this->filesystem->unlink($packageArchive); $this->filesystem->unlink($packageArchive);
} }

View File

@ -184,6 +184,9 @@ class JsonFile
{ {
if (version_compare(PHP_VERSION, '5.4', '>=')) { if (version_compare(PHP_VERSION, '5.4', '>=')) {
$json = json_encode($data, $options); $json = json_encode($data, $options);
if (false === $json) {
self::throwEncodeError(json_last_error());
}
// compact brackets to follow recent php versions // 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', '<'))) { 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); $json = json_encode($data);
if (false === $json) {
self::throwEncodeError(json_last_error());
}
$prettyPrint = (bool) ($options & self::JSON_PRETTY_PRINT); $prettyPrint = (bool) ($options & self::JSON_PRETTY_PRINT);
$unescapeUnicode = (bool) ($options & self::JSON_UNESCAPED_UNICODE); $unescapeUnicode = (bool) ($options & self::JSON_UNESCAPED_UNICODE);
@ -209,6 +215,34 @@ class JsonFile
return $result; 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. * Parses json string and returns hash.
* *
@ -219,6 +253,9 @@ class JsonFile
*/ */
public static function parseJson($json, $file = null) public static function parseJson($json, $file = null)
{ {
if (null === $json) {
return;
}
$data = json_decode($json, true); $data = json_decode($json, true);
if (null === $data && JSON_ERROR_NONE !== json_last_error()) { if (null === $data && JSON_ERROR_NONE !== json_last_error()) {
self::validateSyntax($json, $file); 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 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)) && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch))
&& (stripos($targetPrefix, $sourcePrefix) !== 0) && (stripos($targetPrefix, $sourcePrefix) !== 0)
) { ) {

View File

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

View File

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

View File

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

View File

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

View File

@ -79,6 +79,9 @@ class PluginManager
*/ */
public function addPlugin(PluginInterface $plugin) public function addPlugin(PluginInterface $plugin)
{ {
if ($this->io->isDebug()) {
$this->io->writeError('Loading plugin '.get_class($plugin));
}
$this->plugins[] = $plugin; $this->plugins[] = $plugin;
$plugin->activate($this->composer, $this->io); $plugin->activate($this->composer, $this->io);
@ -127,7 +130,7 @@ class PluginManager
} }
if (!$requiresComposer->matches(new VersionConstraint('==', $this->versionParser->normalize(PluginInterface::PLUGIN_API_VERSION)))) { 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); $this->registerPackage($package);

View File

@ -60,14 +60,14 @@ class ArtifactRepository extends ArrayRepository
$package = $this->getComposerInformation($file); $package = $this->getComposerInformation($file);
if (!$package) { if (!$package) {
if ($io->isVerbose()) { 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; continue;
} }
if ($io->isVerbose()) { if ($io->isVerbose()) {
$template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>'; $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); $this->addPackage($package);

View File

@ -434,7 +434,7 @@ class ComposerRepository extends ArrayRepository
} }
if (!empty($data['warning'])) { 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'])) { if (!empty($data['providers-lazy-url'])) {
@ -613,8 +613,8 @@ class ComposerRepository extends ArrayRepository
if ($cacheKey && ($contents = $this->cache->read($cacheKey))) { if ($cacheKey && ($contents = $this->cache->read($cacheKey))) {
if (!$this->degradedMode) { if (!$this->degradedMode) {
$this->io->write('<warning>'.$e->getMessage().'</warning>'); $this->io->writeError('<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>'.$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; $this->degradedMode = true;
$data = JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey); $data = JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey);

View File

@ -66,13 +66,13 @@ class PearRepository extends ArrayRepository
{ {
parent::initialize(); parent::initialize();
$this->io->write('Initializing PEAR repository '.$this->url); $this->io->writeError('Initializing PEAR repository '.$this->url);
$reader = new ChannelReader($this->rfs); $reader = new ChannelReader($this->rfs);
try { try {
$channelInfo = $reader->read($this->url); $channelInfo = $reader->read($this->url);
} catch (\Exception $e) { } 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; return;
} }
@ -98,7 +98,7 @@ class PearRepository extends ArrayRepository
$normalizedVersion = $versionParser->normalize($version); $normalizedVersion = $versionParser->normalize($version);
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException $e) {
if ($this->io->isVerbose()) { 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; continue;
} }

View File

@ -149,7 +149,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
if (!extension_loaded('openssl')) { if (!extension_loaded('openssl')) {
if ($io->isVerbose()) { 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; return false;

View File

@ -66,7 +66,7 @@ class GitDriver extends VcsDriver
}; };
$gitUtil->runCommand($commandCallable, $this->url, $this->repoDir); $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir);
} catch (\Exception $e) { } 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 { } else {
// clean up directory and do a fresh clone into it // clean up directory and do a fresh clone into it
@ -242,7 +242,7 @@ class GitDriver extends VcsDriver
} }
$process = new ProcessExecutor($io); $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; return true;
} }

View File

@ -266,7 +266,7 @@ class GitHubDriver extends VcsDriver
if (!extension_loaded('openssl')) { if (!extension_loaded('openssl')) {
if ($io->isVerbose()) { 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; return false;
@ -333,7 +333,7 @@ class GitHubDriver extends VcsDriver
if (!$this->io->hasAuthentication($this->originUrl)) { if (!$this->io->hasAuthentication($this->originUrl)) {
if (!$this->io->isInteractive()) { 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; throw $e;
} }
@ -344,7 +344,7 @@ class GitHubDriver extends VcsDriver
if ($rateLimited) { if ($rateLimited) {
$rateLimit = $this->getRateLimit($e->getHeaders()); $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>', '<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['limit'],
$rateLimit['reset'] $rateLimit['reset']
@ -435,7 +435,7 @@ class GitHubDriver extends VcsDriver
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
$this->gitDriver = null; $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; throw $e;
} }
} }

View File

@ -159,7 +159,7 @@ class HgBitbucketDriver extends VcsDriver
if (!extension_loaded('openssl')) { if (!extension_loaded('openssl')) {
if ($io->isVerbose()) { 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; return false;

View File

@ -50,7 +50,7 @@ class HgDriver extends VcsDriver
// update the repo if it is a valid hg repository // 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 (is_dir($this->repoDir) && 0 === $this->process->execute('hg summary', $output, $this->repoDir)) {
if (0 !== $this->process->execute('hg pull', $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 { } else {
// clean up directory and do a fresh clone into it // clean up directory and do a fresh clone into it

View File

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

View File

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

View File

@ -12,44 +12,13 @@
namespace Composer\Script; namespace Composer\Script;
use Composer\Composer; use Composer\Installer\PackageEvent as BasePackageEvent;
use Composer\IO\IOInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
/** /**
* The Package Event. * 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'; const POST_STATUS_CMD = 'post-status-cmd';
/** /** Deprecated constants below */
* 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';
/** /**
* The PRE_AUTOLOAD_DUMP event occurs before the autoload file is generated. * The PRE_AUTOLOAD_DUMP event occurs before the autoload file is generated.
@ -182,4 +130,64 @@ class ScriptEvents
* @var string * @var string
*/ */
const POST_ARCHIVE_CMD = 'post-archive-cmd'; 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 // public github, autoswitch protocols
if (preg_match('{^(?:https?|git)://'.self::getGitHubDomainsRegex($this->config).'/(.*)}', $url, $match)) { 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(); $messages = array();
foreach ($protocols as $protocol) { foreach ($protocols as $protocol) {
if ('ssh' === $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); $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); $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 // private github repository without git access, try https with auth
if (preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url, $match)) { if (preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url, $match)) {
if (!$this->io->hasAuthentication($match[1])) { 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( $auth = array(
'username' => $this->io->ask(' Username: ', $defaultUsername), 'username' => $this->io->ask(' Username: ', $defaultUsername),
'password' => $this->io->askAndHideAnswer(' Password: '), 'password' => $this->io->askAndHideAnswer(' Password: '),
@ -160,18 +164,22 @@ class Git
// added in git 1.7.1, prevents prompting the user for username/password // added in git 1.7.1, prevents prompting the user for username/password
if (getenv('GIT_ASKPASS') !== 'echo') { if (getenv('GIT_ASKPASS') !== 'echo') {
putenv('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 // clean up rogue git env vars in case this is running in a git hook
if (getenv('GIT_DIR')) { if (getenv('GIT_DIR')) {
putenv('GIT_DIR'); putenv('GIT_DIR');
unset($_SERVER['GIT_DIR']);
} }
if (getenv('GIT_WORK_TREE')) { if (getenv('GIT_WORK_TREE')) {
putenv('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 // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
putenv("DYLD_LIBRARY_PATH"); putenv("DYLD_LIBRARY_PATH");
unset($_SERVER['DYLD_LIBRARY_PATH']);
} }
public static function getGitHubDomainsRegex(Config $config) public static function getGitHubDomainsRegex(Config $config)

View File

@ -77,11 +77,11 @@ class GitHub
public function authorizeOAuthInteractively($originUrl, $message = null) public function authorizeOAuthInteractively($originUrl, $message = null)
{ {
if ($message) { 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->writeError(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('To revoke access to this token you can visit https://github.com/settings/applications');
$otp = null; $otp = null;
$attemptCounter = 0; $attemptCounter = 0;
@ -105,13 +105,13 @@ class GitHub
} }
if (401 === $e->getCode()) { if (401 === $e->getCode()) {
$this->io->write('Bad credentials.'); $this->io->writeError('Bad credentials.');
} else { } 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->writeError('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('Add it using "composer config github-oauth.github.com <token>"');
continue; continue;
} }
@ -166,7 +166,7 @@ class GitHub
) )
)); ));
$this->io->write('Token successfully created'); $this->io->writeError('Token successfully created');
return JsonFile::parseJson($json); return JsonFile::parseJson($json);
} }
@ -184,14 +184,14 @@ class GitHub
list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1))); list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1)));
if ('required' === $required) { if ('required' === $required) {
$this->io->write('Two-factor Authentication'); $this->io->writeError('Two-factor Authentication');
if ('app' === $method) { 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) { 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: '); return $this->io->ask('Authentication Code: ');

View File

@ -45,7 +45,7 @@ class ProcessExecutor
{ {
if ($this->io && $this->io->isDebug()) { if ($this->io && $this->io->isDebug()) {
$safeCommand = preg_replace('{(://[^:/\s]+:)[^@\s/]+}i', '$1****', $command); $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 // 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->captureOutput = count(func_get_args()) > 1;
$this->errorOutput = null; $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'); $callback = is_callable($output) ? $output : array($this, 'outputHandler');
$process->run($callback); $process->run($callback);

View File

@ -146,7 +146,7 @@ class RemoteFilesystem
$options = $this->getOptionsForUrl($originUrl, $additionalOptions); $options = $this->getOptionsForUrl($originUrl, $additionalOptions);
if ($this->io->isDebug()) { 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'])) { if (isset($options['github-token'])) {
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$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'))); $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
if ($this->progress) { if ($this->progress) {
$this->io->write(" Downloading: <comment>connection...</comment>", false); $this->io->writeError(" Downloading: <comment>Connecting...</comment>", false);
} }
$errorMessage = ''; $errorMessage = '';
@ -228,7 +228,7 @@ class RemoteFilesystem
} }
if ($this->progress && !$this->retry) { 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 // handle copy command if download was successful
@ -327,9 +327,9 @@ class RemoteFilesystem
$progression = round($bytesTransferred / $this->bytesMax * 100); $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->lastProgress = $progression;
$this->io->overwrite(" Downloading: <comment>$progression%</comment>", false); $this->io->overwriteError(" Downloading: <comment>$progression%</comment>", false);
} }
} }
break; break;
@ -371,7 +371,7 @@ class RemoteFilesystem
throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); 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: '); $username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: '); $password = $this->io->askAndHideAnswer(' Password: ');
$this->io->setAuthentication($this->originUrl, $username, $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 // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
putenv("DYLD_LIBRARY_PATH"); putenv("DYLD_LIBRARY_PATH");
unset($_SERVER['DYLD_LIBRARY_PATH']);
} }
/** /**
@ -111,7 +112,7 @@ class Svn
} }
$output .= $buffer; $output .= $buffer;
if ($verbose) { if ($verbose) {
$io->write($buffer, false); $io->writeError($buffer, false);
} }
}; };
$status = $this->process->execute($svnCommand, $handler, $cwd); $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->hasAuth = true;
$this->credentials['username'] = $this->io->ask("Username: "); $this->credentials['username'] = $this->io->ask("Username: ");

View File

@ -42,7 +42,8 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase
} }
if ($this->oldenv) { if ($this->oldenv) {
$fs->removeDirectory(getenv('COMPOSER_HOME')); $fs->removeDirectory(getenv('COMPOSER_HOME'));
putenv('COMPOSER_HOME='.$this->oldenv); $_SERVER['COMPOSER_HOME'] = $this->oldenv;
putenv('COMPOSER_HOME='.$_SERVER['COMPOSER_HOME']);
$this->oldenv = null; $this->oldenv = null;
} }
} }
@ -86,10 +87,11 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase
$testData = $this->parseTestFile($testFile); $testData = $this->parseTestFile($testFile);
$this->oldenv = getenv('COMPOSER_HOME'); $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']; $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(); $exitcode = $proc->run();
if (isset($testData['EXPECT'])) { if (isset($testData['EXPECT'])) {

View File

@ -29,7 +29,7 @@ class ApplicationTest extends TestCase
->will($this->returnValue('list')); ->will($this->returnValue('list'));
$outputMock->expects($this->once()) $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']))); ->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')) { if (!defined('COMPOSER_DEV_WARNING_TIME')) {

View File

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

View File

@ -74,6 +74,13 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', '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; return $data;
} }
@ -128,7 +135,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
$msg = ''; $msg = '';
$io->expects($this->once()) $io->expects($this->once())
->method('write') ->method('writeError')
->will($this->returnCallback(function ($text) use (&$msg) { ->will($this->returnCallback(function ($text) use (&$msg) {
$msg = $text; $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