Merge branch '2.0' into fix/unused
commit
8dfadd99d1
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
|
@ -10,3 +10,8 @@
|
|||
|
||||
# Exclude non-essential files from dist
|
||||
/tests export-ignore
|
||||
.github export-ignore
|
||||
.php_cs export-ignore
|
||||
.travis.yml export-ignore
|
||||
appveyor.yml export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
|
|
|
@ -21,6 +21,11 @@ If your issue involves installing, updating or resolving dependencies, the
|
|||
chance of us being able to reproduce your issue will be much higher if you
|
||||
share your `composer.json` with us.
|
||||
|
||||
Coding Style Fixes
|
||||
------------------
|
||||
|
||||
We do not accept CS fixes pull requests. Fixes are done by the project maintainers when appropriate to avoid causing too many unnecessary conflicts between branches and pull requests.
|
||||
|
||||
Security Reports
|
||||
----------------
|
||||
|
||||
|
|
11
.php_cs
11
.php_cs
|
@ -20,17 +20,16 @@ $finder = PhpCsFixer\Finder::create()
|
|||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setUsingCache(true)
|
||||
//->setUsingLinter(false)
|
||||
->setRiskyAllowed(true)
|
||||
->setRules(array(
|
||||
'@PSR2' => true,
|
||||
'array_syntax' => array('syntax' => 'long'),
|
||||
'binary_operator_spaces' => true,
|
||||
'blank_line_before_return' => true,
|
||||
'cast_spaces' => true,
|
||||
'blank_line_before_statement' => array('statements' => array('declare', 'return')),
|
||||
'cast_spaces' => array('space' => 'single'),
|
||||
'header_comment' => array('header' => $header),
|
||||
'include' => true,
|
||||
'array_syntax' => array('syntax' => 'long'),
|
||||
'method_separation' => true,
|
||||
'class_attributes_separation' => array('elements' => array('method')),
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'no_blank_lines_after_phpdoc' => true,
|
||||
'no_empty_statement' => true,
|
||||
|
@ -39,7 +38,6 @@ return PhpCsFixer\Config::create()
|
|||
'no_leading_namespace_whitespace' => true,
|
||||
'no_trailing_comma_in_singleline_array' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'object_operator_without_whitespace' => true,
|
||||
'phpdoc_align' => true,
|
||||
|
@ -52,7 +50,6 @@ return PhpCsFixer\Config::create()
|
|||
'phpdoc_types' => true,
|
||||
'psr0' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'short_scalar_cast' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trailing_comma_in_multiline_array' => true,
|
||||
|
|
44
.travis.yml
44
.travis.yml
|
@ -1,7 +1,5 @@
|
|||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: trusty
|
||||
|
||||
git:
|
||||
|
@ -16,34 +14,41 @@ addons:
|
|||
packages:
|
||||
- parallel
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dist: precise
|
||||
php: 5.3
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
- php: 5.4
|
||||
- php: 5.5
|
||||
- php: 5.6
|
||||
- php: 7.0
|
||||
- php: 7.1
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
env: PHPSTAN=1
|
||||
- php: 7.3
|
||||
env:
|
||||
- deps=high
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
|
||||
before_install:
|
||||
# determine INI file
|
||||
- if [[ $TRAVIS_PHP_VERSION = hhvm* ]]; then export INI=/etc/hhvm/php.ini; else export INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi
|
||||
# disable xdebug if available
|
||||
- phpenv config-rm xdebug.ini || echo "xdebug not available"
|
||||
# disable default memory limit
|
||||
- export INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
|
||||
- echo memory_limit = -1 >> $INI
|
||||
- composer validate
|
||||
|
||||
install:
|
||||
# flags to pass to install
|
||||
- flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress"
|
||||
# update deps to latest in case of high deps build
|
||||
- if [ "$deps" == "high" ]; then composer config platform.php 7.2.4; composer update $flags; fi
|
||||
# install dependencies using system provided composer binary
|
||||
- composer install $flags
|
||||
# install dependencies using composer from source
|
||||
|
@ -56,7 +61,12 @@ before_script:
|
|||
|
||||
script:
|
||||
# run test suite directories in parallel using GNU parallel
|
||||
- ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && exit 1);'
|
||||
- ls -d tests/Composer/Test/* | grep -v TestCase.php | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && exit 1);'
|
||||
# Run PHPStan
|
||||
- if [[ $PHPSTAN == "1" ]]; then
|
||||
bin/composer require --dev phpstan/phpstan-shim:^0.11 --ignore-platform-reqs &&
|
||||
vendor/bin/phpstan.phar analyse src tests --configuration=phpstan/config.neon --autoload-file=phpstan/autoload.php;
|
||||
fi
|
||||
|
||||
before_deploy:
|
||||
- php -d phar.readonly=0 bin/compile
|
||||
|
@ -69,4 +79,4 @@ deploy:
|
|||
on:
|
||||
tags: true
|
||||
repo: composer/composer
|
||||
php: '7.1'
|
||||
php: '7.2'
|
||||
|
|
171
CHANGELOG.md
171
CHANGELOG.md
|
@ -1,3 +1,152 @@
|
|||
### [1.8.6] 2019-06-11
|
||||
|
||||
* Fixed handling of backslash-escapes handling in composer.json when using the require command
|
||||
* Fixed create-project not following classmap-authoritative and apcu-autoloader config values
|
||||
* Fixed HHVM version warning showing up in some cases when it was not in use
|
||||
|
||||
### [1.8.5] 2019-04-09
|
||||
|
||||
* HHVM 4.0 is no longer compatible with Composer. Please use PHP instead going forward.
|
||||
* Added forward compatibility with upcoming 2.0 changes
|
||||
* Fixed support for PHP 7.3-style heredoc/nowdoc syntax changes in autoload generation
|
||||
* Fixed require command usage when combined with --ignore-platform-reqs
|
||||
* Fixed and cleaned up various Windows junctions handling issues
|
||||
|
||||
### [1.8.4] 2019-02-11
|
||||
|
||||
* Fixed long standing solver bug leading to odd solving issues in edge cases, see #7946
|
||||
* Fixed HHVM support for upcoming releases
|
||||
* Fixed unix proxy for binaries to be POSIX compatible instead of breaking some shells
|
||||
* Fixed invalid deprecation warning for composer-plugin-api
|
||||
* Fixed edge case issues with Windows junctions when working with path repositories
|
||||
|
||||
### [1.8.3] 2019-01-30
|
||||
|
||||
* Fixed regression when executing partial updates
|
||||
|
||||
### [1.8.2] 2019-01-29
|
||||
|
||||
* Fixed invalid deprecation warning for ext-pdo_mysql and similar
|
||||
* Updated to latest xdebug-handler
|
||||
|
||||
### [1.8.1] 2019-01-29
|
||||
|
||||
* Deprecated support for non-standard package names (anything with uppercase, or no / in it). Make sure to follow the warnings if you see any to avoid problems in 2.0.
|
||||
* Fixed some packages missing from the autoloader config when installing with --no-dev
|
||||
* Fixed support for cloning GitLab repos using OAuth tokens instead of SSH keys
|
||||
* Fixed metapackage installs/updates missing from output
|
||||
* Fixed --with-dependencies / --with-all-dependencies not updating some packages in some edge cases
|
||||
* Fixed compatibility with Symfony 4.2 deprecations
|
||||
* Fixed temp dir not being cleaned up on download error while archiving packages
|
||||
* Updated to latest ca-bundle
|
||||
|
||||
### [1.8.0] 2018-12-03
|
||||
|
||||
* Changed `post-package-install` / `post-package-update` event to be fired *after* the lock file has been updated as opposed to before
|
||||
* Added support for removing packages using a wildcard with the `remove` command, e.g. `composer remove foo/*`
|
||||
* Added `chat` to the list of `support` channels you can list in composer.json
|
||||
* Added signal handling on require command to restore the composer.json in case of abort
|
||||
* Added `--ignore` to `outdated` command to pass one or more packages that you do not want to be listed
|
||||
* Added `--no-dev` to `check-platform-reqs` command to skip dev requirements even if they are installed
|
||||
* Added support for running plugin commands from sub-directories within a project much like other Composer commands
|
||||
* Added support for running Composer via phpdbg
|
||||
* Added `lib-imagick` platform package
|
||||
* Fixed validate command always checking for disabled checks when used with `--strict`
|
||||
|
||||
### [1.7.3] 2018-11-01
|
||||
|
||||
* Fixed handling of replace/conflict rules. This may affect dependency resolution in some edge cases.
|
||||
* Fixed Bitbucket API support and migrated all calls to API v2 as v1 is deprecated
|
||||
* Fixed support for lib-openssl 1.1.1 having only lowercase algorithm names
|
||||
* Fixed escaping of URLs in Perforce and Svn drivers
|
||||
* Fixed `show` command not respecting `--path` when a single package name was given
|
||||
* Fixed regression in 1.7.2's handling of metapackages
|
||||
|
||||
### [1.7.2] 2018-08-16
|
||||
|
||||
* Fixed reporting of authentication/rate limiting issues for GitHub API access
|
||||
* Fixed `create-project` not checking the checking the latest commit out when a cache was already present
|
||||
* Fixed reporting of errors when `global` command can not switch the working directory
|
||||
* Fixed PHP 5.3 JSON encoding issues with complex unicode character sequences
|
||||
* Updated to latest ca-bundle and xdebug-handler projects, see related changelogs
|
||||
|
||||
### [1.7.1] 2018-08-07
|
||||
|
||||
* Fixed issue autoloading plugins in require-dev in some conditions
|
||||
* Fixed handling of SSL to repo.packagist.org on very old PHP versions
|
||||
|
||||
### [1.7.0] 2018-08-03
|
||||
|
||||
* Added the overridden platform config's PHP version in the `diagnose` command output
|
||||
* Fixed --no-plugins not being respected in a few commands
|
||||
* Fixed 1.7.0-RC regression in output showing <warn> instead of proper colors
|
||||
* Fixed 1.7.0-RC regression in output missing "Loading from cache" output on package install
|
||||
|
||||
### [1.7.0-RC] 2018-07-24
|
||||
|
||||
* Changed default repository URL from packagist.org to repo.packagist.org, this might affect people with strict firewall rules
|
||||
* Changed output from Updating to Downgrading when performing package downgrades, this might affect anything parsing output
|
||||
* Several minor performance improvements
|
||||
* Added basic authentication support for mercurial repos
|
||||
* Added explicit `i` and `u` aliases for the `install` and `update` commands
|
||||
* Added support for `show` command to output json format with --tree
|
||||
* Added support for {glob,braces} support in the path repository's path argument
|
||||
* Added support in `status` command for showing diffs in vendor dir even for packages installed as dist/zip archives
|
||||
* Added `--remove-vcs` flag to `create-project` command to avoid prompting for keeping VCS files
|
||||
* Added `--no-secure-http` flag to `create-project` command to bypass https (use at your own risk)
|
||||
* Added `pre-command-run` event that lets plugins modify arguments
|
||||
* Added RemoteFilesystem::getRemoteContents extension point
|
||||
* Fixed setting scripts via `config` command
|
||||
|
||||
### [1.6.5] 2018-05-04
|
||||
|
||||
* Fixed regression in 1.6.4 causing strange update behaviors with dev packages
|
||||
* Fixed regression in 1.6.4 color support detection for Windows
|
||||
* Fixed issues dealing with broken symlinks when switching branches and using path repositories
|
||||
* Fixed JSON schema for package repositories
|
||||
* Fixed issues on computers set to Turkish locale
|
||||
* Fixed classmap parsing of files using short-open-tags when they are disabled in php
|
||||
|
||||
### [1.6.4] 2018-04-13
|
||||
|
||||
* Security fixes in some edge case scenarios, recommended update for all users
|
||||
* Fixed regression in version guessing of path repositories
|
||||
* Fixed removing aliased packages from the repository, which might resolve some odd update bugs
|
||||
* Fixed updating of package URLs for GitLab
|
||||
* Fixed run-script --list failing when script handlers were defined
|
||||
* Fixed init command not respecting the current php version when selecting package versions
|
||||
* Fixed handling of uppercase package names in why/why-not commands
|
||||
* Fixed exclude-from-classmap symlink handling
|
||||
* Fixed filesystem permissions of PEAR binaries
|
||||
* Improved performance of subversion repos
|
||||
* Other minor fixes
|
||||
|
||||
### [1.6.3] 2018-01-31
|
||||
|
||||
* Fixed GitLab downloads failing in some edge cases
|
||||
* Fixed ctrl-C handling during create-project
|
||||
* Fixed GitHub VCS repositories not prompting for a token in some conditions
|
||||
* Fixed SPDX license identifiers being case sensitive
|
||||
* Fixed and clarified a few dependency resolution error reporting strings
|
||||
* Fixed SVN commit log fetching in verbose mode when using private repositories
|
||||
|
||||
### [1.6.2] 2018-01-05
|
||||
|
||||
* Fixed more autoloader regressions
|
||||
* Fixed support for updating dist refs in gitlab URLs
|
||||
|
||||
### [1.6.1] 2018-01-04
|
||||
|
||||
* Fixed upgrade regression due to some autoloader cleanups
|
||||
* Fixed some overly loose version constraints
|
||||
|
||||
### [1.6.0] 2018-01-04
|
||||
|
||||
* Added support for SPDX license identifiers v3.0, deprecates GPL/LGPL/AGPL identifiers, which should now have a `-only` or `-or-later` suffix added.
|
||||
* Added support for COMPOSER_MEMORY_LIMIT env var to make Composer set the PHP memory limit explicitly
|
||||
* Added support for simple strings for the `bin`
|
||||
* Fixed `check-platform-reqs` bug in version checking
|
||||
|
||||
### [1.6.0-RC] 2017-12-19
|
||||
|
||||
* Improved performance of installs and updates from git clones when checking out known commits
|
||||
|
@ -143,7 +292,7 @@
|
|||
* Added `COMPOSER_MIRROR_PATH_REPOS` env var to force mirroring of path repositories vs symlinking
|
||||
* Added `COMPOSER_DEV_MODE` env var that is set by Composer to forward the dev mode to script handlers
|
||||
* Fixed support for git 2.11
|
||||
* Fixed output from zip and rar leaking out when an error occured
|
||||
* Fixed output from zip and rar leaking out when an error occurred
|
||||
* Removed `hash` from composer.lock, only `content-hash` is now used which should reduce conflicts
|
||||
* Minor fixes and performance improvements
|
||||
|
||||
|
@ -578,7 +727,7 @@
|
|||
* Added autoloading support for root packages that use target-dir
|
||||
* Added awareness of the root package presence and support for it's provide/replace/conflict keys
|
||||
* Added IOInterface::isDecorated to test for colored output support
|
||||
* Added validation of licenses based on the [SPDX registry](http://www.spdx.org/licenses/)
|
||||
* Added validation of licenses based on the [SPDX registry](https://spdx.org/licenses/)
|
||||
* Improved repository protocol to have large cacheable parts
|
||||
* Fixed various bugs relating to package aliasing, proxy configuration, binaries
|
||||
* Various bug fixes and docs improvements
|
||||
|
@ -602,6 +751,24 @@
|
|||
|
||||
* Initial release
|
||||
|
||||
[1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
|
||||
[1.8.5]: https://github.com/composer/composer/compare/1.8.4...1.8.5
|
||||
[1.8.4]: https://github.com/composer/composer/compare/1.8.3...1.8.4
|
||||
[1.8.3]: https://github.com/composer/composer/compare/1.8.2...1.8.3
|
||||
[1.8.2]: https://github.com/composer/composer/compare/1.8.1...1.8.2
|
||||
[1.8.1]: https://github.com/composer/composer/compare/1.8.0...1.8.1
|
||||
[1.8.0]: https://github.com/composer/composer/compare/1.7.3...1.8.0
|
||||
[1.7.3]: https://github.com/composer/composer/compare/1.7.2...1.7.3
|
||||
[1.7.2]: https://github.com/composer/composer/compare/1.7.1...1.7.2
|
||||
[1.7.1]: https://github.com/composer/composer/compare/1.7.0...1.7.1
|
||||
[1.7.0]: https://github.com/composer/composer/compare/1.7.0-RC...1.7.0
|
||||
[1.7.0-RC]: https://github.com/composer/composer/compare/1.6.5...1.7.0-RC
|
||||
[1.6.5]: https://github.com/composer/composer/compare/1.6.4...1.6.5
|
||||
[1.6.4]: https://github.com/composer/composer/compare/1.6.3...1.6.4
|
||||
[1.6.3]: https://github.com/composer/composer/compare/1.6.2...1.6.3
|
||||
[1.6.2]: https://github.com/composer/composer/compare/1.6.1...1.6.2
|
||||
[1.6.1]: https://github.com/composer/composer/compare/1.6.0...1.6.1
|
||||
[1.6.0]: https://github.com/composer/composer/compare/1.6.0-RC...1.6.0
|
||||
[1.6.0-RC]: https://github.com/composer/composer/compare/1.5.6...1.6.0-RC
|
||||
[1.5.6]: https://github.com/composer/composer/compare/1.5.5...1.5.6
|
||||
[1.5.5]: https://github.com/composer/composer/compare/1.5.4...1.5.5
|
||||
|
|
|
@ -6,8 +6,6 @@ Composer helps you declare, manage, and install dependencies of PHP projects.
|
|||
See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation.
|
||||
|
||||
[![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer)
|
||||
[![Dependency Status](https://www.versioneye.com/php/composer:composer/dev-master/badge.svg)](https://www.versioneye.com/php/composer:composer/dev-master)
|
||||
[![Reference Status](https://www.versioneye.com/php/composer:composer/reference_badge.svg?style=flat)](https://www.versioneye.com/php/composer:composer/references)
|
||||
|
||||
Installation / Usage
|
||||
--------------------
|
||||
|
@ -62,5 +60,3 @@ Acknowledgments
|
|||
|
||||
- This project's Solver started out as a PHP port of openSUSE's
|
||||
[Libzypp satsolver](https://en.opensuse.org/openSUSE:Libzypp_satsolver).
|
||||
- This project uses hiddeninput.exe to prompt for passwords on windows, sources
|
||||
and details can be found on the [github page of the project](https://github.com/Seldaek/hidden-input).
|
||||
|
|
20
appveyor.yml
20
appveyor.yml
|
@ -2,23 +2,27 @@ build: false
|
|||
clone_depth: 5
|
||||
|
||||
environment:
|
||||
PHP_CHOCO_VERSION: 7.2.0
|
||||
PHP_CACHE_DIR: C:\tools\php
|
||||
# This sets the PHP version (from Chocolatey)
|
||||
PHPCI_CHOCO_VERSION: 7.3.1
|
||||
PHPCI_CACHE: C:\tools\phpci
|
||||
PHPCI_PHP: C:\tools\phpci\php
|
||||
PHPCI_COMPOSER: C:\tools\phpci\composer
|
||||
|
||||
cache:
|
||||
- '%PHP_CACHE_DIR% -> appveyor.yml'
|
||||
- '%PHPCI_CACHE% -> appveyor.yml'
|
||||
|
||||
init:
|
||||
- SET PATH=%PHP_CACHE_DIR%;%PATH%
|
||||
- SET COMPOSER_CACHE_DIR=%PHP_CACHE_DIR%
|
||||
- SET PATH=%PHPCI_PHP%;%PHPCI_COMPOSER%;%PATH%
|
||||
- SET COMPOSER_HOME=%PHPCI_COMPOSER%\home
|
||||
- SET COMPOSER_CACHE_DIR=%PHPCI_COMPOSER%\cache
|
||||
- SET COMPOSER_NO_INTERACTION=1
|
||||
- SET PHP=0
|
||||
- SET ANSICON=121x90 (121x90)
|
||||
|
||||
install:
|
||||
- IF EXIST %PHP_CACHE_DIR% (SET PHP=1)
|
||||
- IF %PHP%==0 cinst php -y --version %PHP_CHOCO_VERSION% --params "/InstallDir:%PHP_CACHE_DIR%"
|
||||
- IF %PHP%==0 cinst composer -y --ia "/DEV=%PHP_CACHE_DIR%"
|
||||
- IF EXIST %PHPCI_CACHE% (SET PHP=1)
|
||||
- IF %PHP%==0 cinst php -i -y --version %PHPCI_CHOCO_VERSION% --params "/InstallDir:%PHPCI_PHP%"
|
||||
- IF %PHP%==0 cinst composer -i -y --ia "/DEV=%PHPCI_COMPOSER%"
|
||||
- php -v
|
||||
- IF %PHP%==0 (composer --version) ELSE (composer self-update)
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
|
|
23
bin/composer
23
bin/composer
|
@ -1,25 +1,28 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
|
||||
echo 'Warning: Composer should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
|
||||
}
|
||||
|
||||
setlocale(LC_ALL, 'C');
|
||||
require __DIR__.'/../src/bootstrap.php';
|
||||
|
||||
use Composer\Factory;
|
||||
use Composer\XdebugHandler;
|
||||
use Composer\Console\Application;
|
||||
use Composer\XdebugHandler\XdebugHandler;
|
||||
|
||||
error_reporting(-1);
|
||||
|
||||
// Create output for XdebugHandler and Application
|
||||
$output = Factory::createOutput();
|
||||
|
||||
$xdebug = new XdebugHandler($output);
|
||||
// Restart without xdebug
|
||||
$xdebug = new XdebugHandler('Composer', '--ansi');
|
||||
$xdebug->check();
|
||||
unset($xdebug);
|
||||
|
||||
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '4.0', '>=')) {
|
||||
echo 'HHVM 4.0 has dropped support for Composer, please use PHP instead. Aborting.'.PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (function_exists('ini_set')) {
|
||||
@ini_set('display_errors', 1);
|
||||
|
||||
|
@ -45,6 +48,10 @@ if (function_exists('ini_set')) {
|
|||
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1536) {
|
||||
@ini_set('memory_limit', '1536M');
|
||||
}
|
||||
// Set user defined memory limit
|
||||
if ($memoryLimit = getenv('COMPOSER_MEMORY_LIMIT')) {
|
||||
@ini_set('memory_limit', $memoryLimit);
|
||||
}
|
||||
unset($memoryInBytes, $memoryLimit);
|
||||
}
|
||||
|
||||
|
@ -52,4 +59,4 @@ putenv('COMPOSER_BINARY='.realpath($_SERVER['argv'][0]));
|
|||
|
||||
// run the command application
|
||||
$application = new Application();
|
||||
$application->run(null, $output);
|
||||
$application->run();
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
{
|
||||
"name": "composer/composer",
|
||||
"description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
|
||||
"keywords": ["package", "dependency", "autoload"],
|
||||
"homepage": "https://getcomposer.org/",
|
||||
"type": "library",
|
||||
"description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
|
||||
"keywords": [
|
||||
"package",
|
||||
"dependency",
|
||||
"autoload"
|
||||
],
|
||||
"homepage": "https://getcomposer.org/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
|
@ -17,52 +21,67 @@
|
|||
"homepage": "http://seld.be"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/composer",
|
||||
"issues": "https://github.com/composer/composer/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0",
|
||||
"justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"composer/semver": "^1.0",
|
||||
"composer/spdx-licenses": "^1.0",
|
||||
"composer/spdx-licenses": "^1.2",
|
||||
"composer/xdebug-handler": "^1.1",
|
||||
"justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
|
||||
"psr/log": "^1.0",
|
||||
"seld/jsonlint": "^1.4",
|
||||
"seld/phar-utils": "^1.0",
|
||||
"symfony/console": "^2.7 || ^3.0 || ^4.0",
|
||||
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
|
||||
"symfony/finder": "^2.7 || ^3.0 || ^4.0",
|
||||
"symfony/process": "^2.7 || ^3.0 || ^4.0",
|
||||
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
|
||||
"seld/phar-utils": "^1.0",
|
||||
"seld/cli-prompt": "^1.0",
|
||||
"psr/log": "^1.0"
|
||||
"react/promise": "^1.2 || ^2.7"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "2.8.38"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7",
|
||||
"phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
|
||||
"ext-zip": "Enabling the zip extension allows you to unzip archives",
|
||||
"ext-zlib": "Allow gzip compression of HTTP requests"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.3.9"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-zip": "Enabling the zip extension allows you to unzip archives",
|
||||
"ext-zlib": "Allow gzip compression of HTTP requests",
|
||||
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Composer\\": "src/Composer" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Composer\\Test\\": "tests/Composer/Test" }
|
||||
},
|
||||
"bin": ["bin/composer"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.6-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\": "src/Composer"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Composer\\Test\\": "tests/Composer/Test"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/composer"
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "@php -dphar.readonly=0 bin/compile",
|
||||
"test": "phpunit"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"compile": "Compile composer.phar",
|
||||
"test": "Run all tests"
|
||||
},
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/composer/issues",
|
||||
"irc": "irc://irc.freenode.org/composer"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d3c8dbadf8d41e2c7933e274b2fe1327",
|
||||
"content-hash": "280f5d5184039085b5f22236d267ae82",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/ca-bundle",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/ca-bundle.git",
|
||||
"reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288"
|
||||
"reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/943b2c4fcad1ef178d16a713c2468bf7e579c288",
|
||||
"reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288",
|
||||
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
|
||||
"reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -26,7 +26,7 @@
|
|||
"php": "^5.3.2 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
|
||||
"psr/log": "^1.0",
|
||||
"symfony/process": "^2.5 || ^3.0 || ^4.0"
|
||||
},
|
||||
|
@ -60,20 +60,20 @@
|
|||
"ssl",
|
||||
"tls"
|
||||
],
|
||||
"time": "2017-11-29T09:37:33+00:00"
|
||||
"time": "2019-01-28T09:30:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/semver.git",
|
||||
"reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
|
||||
"reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
|
||||
"reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
|
||||
"reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -122,28 +122,27 @@
|
|||
"validation",
|
||||
"versioning"
|
||||
],
|
||||
"time": "2016-08-30T16:08:34+00:00"
|
||||
"time": "2019-03-19T17:25:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/spdx-licenses",
|
||||
"version": "1.1.6",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/spdx-licenses.git",
|
||||
"reference": "2603a0d7ddc00a015deb576fa5297ca43dee6b1c"
|
||||
"reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/spdx-licenses/zipball/2603a0d7ddc00a015deb576fa5297ca43dee6b1c",
|
||||
"reference": "2603a0d7ddc00a015deb576fa5297ca43dee6b1c",
|
||||
"url": "https://api.github.com/repos/composer/spdx-licenses/zipball/a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
|
||||
"reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0"
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.5 || ^5.0.5",
|
||||
"phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -183,29 +182,73 @@
|
|||
"spdx",
|
||||
"validator"
|
||||
],
|
||||
"time": "2017-04-03T19:08:52+00:00"
|
||||
"time": "2019-03-26T10:23:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.6",
|
||||
"name": "composer/xdebug-handler",
|
||||
"version": "1.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "d283e11b6e14c6f4664cf080415c4341293e5bbd"
|
||||
"url": "https://github.com/composer/xdebug-handler.git",
|
||||
"reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/d283e11b6e14c6f4664cf080415c4341293e5bbd",
|
||||
"reference": "d283e11b6e14c6f4664cf080415c4341293e5bbd",
|
||||
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
|
||||
"reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0",
|
||||
"psr/log": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\XdebugHandler\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "John Stevenson",
|
||||
"email": "john-stevenson@blueyonder.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "Restarts a process without xdebug.",
|
||||
"keywords": [
|
||||
"Xdebug",
|
||||
"performance"
|
||||
],
|
||||
"time": "2019-05-27T17:52:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
|
||||
"reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.1",
|
||||
"friendsofphp/php-cs-fixer": "~2.2.20",
|
||||
"json-schema/json-schema-test-suite": "1.2.0",
|
||||
"phpunit/phpunit": "^4.8.22"
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"bin": [
|
||||
"bin/validate-json"
|
||||
|
@ -249,20 +292,20 @@
|
|||
"json",
|
||||
"schema"
|
||||
],
|
||||
"time": "2017-10-21T13:15:38+00:00"
|
||||
"time": "2019-01-14T23:55:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
|
||||
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -296,35 +339,38 @@
|
|||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2016-10-10T12:19:37+00:00"
|
||||
"time": "2018-11-20T15:27:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "seld/cli-prompt",
|
||||
"version": "1.0.3",
|
||||
"name": "react/promise",
|
||||
"version": "v1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/cli-prompt.git",
|
||||
"reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd"
|
||||
"url": "https://github.com/reactphp/promise.git",
|
||||
"reference": "eefff597e67ff66b719f8171480add3c91474a1e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/a19a7376a4689d4d94cab66ab4f3c816019ba8dd",
|
||||
"reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd",
|
||||
"url": "https://api.github.com/repos/reactphp/promise/zipball/eefff597e67ff66b719f8171480add3c91474a1e",
|
||||
"reference": "eefff597e67ff66b719f8171480add3c91474a1e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
"dev-master": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Seld\\CliPrompt\\": "src/"
|
||||
}
|
||||
"psr-0": {
|
||||
"React\\Promise": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/React/Promise/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
@ -332,39 +378,32 @@
|
|||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be"
|
||||
"name": "Jan Sorgalla",
|
||||
"email": "jsorgalla@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type",
|
||||
"keywords": [
|
||||
"cli",
|
||||
"console",
|
||||
"hidden",
|
||||
"input",
|
||||
"prompt"
|
||||
],
|
||||
"time": "2017-03-18T11:32:45+00:00"
|
||||
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
|
||||
"time": "2016-03-07T13:46:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "seld/jsonlint",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/jsonlint.git",
|
||||
"reference": "7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19"
|
||||
"reference": "d15f59a67ff805a44c50ea0516d2341740f81a38"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19",
|
||||
"reference": "7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19",
|
||||
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38",
|
||||
"reference": "d15f59a67ff805a44c50ea0516d2341740f81a38",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.5"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/jsonlint"
|
||||
|
@ -393,7 +432,7 @@
|
|||
"parser",
|
||||
"validator"
|
||||
],
|
||||
"time": "2017-11-30T15:34:22+00:00"
|
||||
"time": "2018-01-24T12:46:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "seld/phar-utils",
|
||||
|
@ -441,16 +480,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de"
|
||||
"reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/46270f1ca44f08ebc134ce120fd2c2baf5fd63de",
|
||||
"reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12",
|
||||
"reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -464,7 +503,7 @@
|
|||
"symfony/process": "~2.1|~3.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
"psr/log-implementation": "For using the console logger",
|
||||
"symfony/event-dispatcher": "",
|
||||
"symfony/process": ""
|
||||
},
|
||||
|
@ -498,20 +537,20 @@
|
|||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-29T09:33:18+00:00"
|
||||
"time": "2018-11-20T15:55:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "e72a0340dc2e273b3c4398d8eef9157ba51d8b95"
|
||||
"reference": "74251c8d50dd3be7c4ce0c7b862497cdc641a5d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/e72a0340dc2e273b3c4398d8eef9157ba51d8b95",
|
||||
"reference": "e72a0340dc2e273b3c4398d8eef9157ba51d8b95",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/74251c8d50dd3be7c4ce0c7b862497cdc641a5d0",
|
||||
"reference": "74251c8d50dd3be7c4ce0c7b862497cdc641a5d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -555,24 +594,25 @@
|
|||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-19T19:05:05+00:00"
|
||||
"time": "2018-11-11T11:18:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b"
|
||||
"reference": "7ae46872dad09dffb7fe1e93a0937097339d0080"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b",
|
||||
"reference": "15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7ae46872dad09dffb7fe1e93a0937097339d0080",
|
||||
"reference": "7ae46872dad09dffb7fe1e93a0937097339d0080",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -604,20 +644,20 @@
|
|||
],
|
||||
"description": "Symfony Filesystem Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-19T18:39:05+00:00"
|
||||
"time": "2018-11-11T11:18:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8"
|
||||
"reference": "1444eac52273e345d9b95129bf914639305a9ba4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/efeceae6a05a9b2fcb3391333f1d4a828ff44ab8",
|
||||
"reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/1444eac52273e345d9b95129bf914639305a9ba4",
|
||||
"reference": "1444eac52273e345d9b95129bf914639305a9ba4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -653,20 +693,78 @@
|
|||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-05T15:25:56+00:00"
|
||||
"time": "2018-11-11T11:18:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.6.0",
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
|
||||
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
|
||||
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -678,7 +776,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.6-dev"
|
||||
"dev-master": "1.11-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -712,20 +810,20 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2017-10-11T12:05:26+00:00"
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "d25449e031f600807949aab7cadbf267712f4eee"
|
||||
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/d25449e031f600807949aab7cadbf267712f4eee",
|
||||
"reference": "d25449e031f600807949aab7cadbf267712f4eee",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
|
||||
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -761,7 +859,7 @@
|
|||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-05T15:25:56+00:00"
|
||||
"time": "2018-11-11T11:18:13+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
@ -870,33 +968,33 @@
|
|||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.7.3",
|
||||
"version": "1.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf"
|
||||
"reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
|
||||
"reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
|
||||
"reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
|
||||
"sebastian/comparator": "^1.1|^2.0",
|
||||
"sebastian/comparator": "^1.1|^2.0|^3.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^2.5|^3.2",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.7.x-dev"
|
||||
"dev-master": "1.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -929,7 +1027,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2017-11-24T13:59:53+00:00"
|
||||
"time": "2018-08-05T17:53:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -1681,20 +1779,21 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.8.32",
|
||||
"version": "v2.8.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "968ef42161e4bc04200119da473077f9e7015128"
|
||||
"reference": "02c1859112aa779d9ab394ae4f3381911d84052b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/968ef42161e4bc04200119da473077f9e7015128",
|
||||
"reference": "968ef42161e4bc04200119da473077f9e7015128",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
|
||||
"reference": "02c1859112aa779d9ab394ae4f3381911d84052b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -1726,7 +1825,7 @@
|
|||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-11-29T09:33:18+00:00"
|
||||
"time": "2018-11-11T11:18:13+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -14,7 +14,7 @@ manager. It does however support a "global" project for convenience via the
|
|||
[global](03-cli.md#global) command.
|
||||
|
||||
This idea is not new and Composer is strongly inspired by node's
|
||||
[npm](https://npmjs.org/) and ruby's [bundler](http://bundler.io/).
|
||||
[npm](https://www.npmjs.com/) and ruby's [bundler](https://bundler.io/).
|
||||
|
||||
Suppose:
|
||||
|
||||
|
@ -40,9 +40,9 @@ To install packages from sources instead of simple zip archives, you will need
|
|||
git, svn, fossil or hg depending on how the package is version-controlled.
|
||||
|
||||
Composer is multi-platform and we strive to make it run equally well on Windows,
|
||||
Linux and OSX.
|
||||
Linux and macOS.
|
||||
|
||||
## Installation - Linux / Unix / OSX
|
||||
## Installation - Linux / Unix / macOS
|
||||
|
||||
### Downloading the Composer Executable
|
||||
|
||||
|
@ -57,16 +57,15 @@ project, or globally as a system wide executable.
|
|||
|
||||
#### Locally
|
||||
|
||||
Installing Composer locally is a matter of just running the installer in your
|
||||
project directory. See [the Download page](https://getcomposer.org/download/)
|
||||
for instructions.
|
||||
To install Composer locally, run the installer in your project directory. See
|
||||
[the Download page](https://getcomposer.org/download/) for instructions.
|
||||
|
||||
The installer will just check a few PHP settings and then download
|
||||
`composer.phar` to your working directory. This file is the Composer binary. It
|
||||
is a PHAR (PHP archive), which is an archive format for PHP which can be run on
|
||||
The installer will check a few PHP settings and then download `composer.phar`
|
||||
to your working directory. This file is the Composer binary. It is a PHAR
|
||||
(PHP archive), which is an archive format for PHP which can be run on
|
||||
the command line, amongst other things.
|
||||
|
||||
Now just run `php composer.phar` in order to run Composer.
|
||||
Now run `php composer.phar` in order to run Composer.
|
||||
|
||||
You can install Composer to a specific directory by using the `--install-dir`
|
||||
option and additionally (re)name it as well using the `--filename` option. When
|
||||
|
@ -78,12 +77,12 @@ following parameters:
|
|||
php composer-setup.php --install-dir=bin --filename=composer
|
||||
```
|
||||
|
||||
Now just run `php bin/composer` in order to run Composer.
|
||||
Now run `php bin/composer` in order to run Composer.
|
||||
|
||||
#### Globally
|
||||
|
||||
You can place the Composer PHAR anywhere you wish. If you put it in a directory
|
||||
that is part of your `PATH`, you can access it globally. On unixy systems you
|
||||
that is part of your `PATH`, you can access it globally. On Unix systems you
|
||||
can even make it executable and invoke it without directly using the `php`
|
||||
interpreter.
|
||||
|
||||
|
@ -94,10 +93,14 @@ you can run this to move composer.phar to a directory that is in your path:
|
|||
mv composer.phar /usr/local/bin/composer
|
||||
```
|
||||
|
||||
If you like to install it only for your user and avoid requiring root permissions,
|
||||
you can use `~/.local/bin` instead which is available by default on some
|
||||
Linux distributions.
|
||||
|
||||
> **Note:** If the above fails due to permissions, you may need to run it again
|
||||
> with sudo.
|
||||
|
||||
> **Note:** On some versions of OSX the `/usr` directory does not exist by
|
||||
> **Note:** On some versions of macOS the `/usr` directory does not exist by
|
||||
> default. If you receive the error "/usr/local/bin/composer: No such file or
|
||||
> directory" then you must create the directory manually before proceeding:
|
||||
> `mkdir -p /usr/local/bin`.
|
||||
|
@ -105,7 +108,7 @@ mv composer.phar /usr/local/bin/composer
|
|||
> **Note:** For information on changing your PATH, please read the
|
||||
> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use Google.
|
||||
|
||||
Now just run `composer` in order to run Composer instead of `php composer.phar`.
|
||||
Now run `composer` in order to run Composer instead of `php composer.phar`.
|
||||
|
||||
## Installation - Windows
|
||||
|
||||
|
@ -115,7 +118,7 @@ This is the easiest way to get Composer set up on your machine.
|
|||
|
||||
Download and run
|
||||
[Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). It will
|
||||
install the latest Composer version and set up your PATH so that you can just
|
||||
install the latest Composer version and set up your PATH so that you can
|
||||
call `composer` from any directory in your command line.
|
||||
|
||||
> **Note:** Close your current terminal. Test usage with a new terminal: This is
|
||||
|
@ -135,7 +138,7 @@ C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat
|
|||
|
||||
Add the directory to your PATH environment variable if it isn't already.
|
||||
For information on changing your PATH variable, please see
|
||||
[this article](http://www.computerhope.com/issues/ch000549.htm) and/or
|
||||
[this article](https://www.computerhope.com/issues/ch000549.htm) and/or
|
||||
use Google.
|
||||
|
||||
Close your current terminal. Test usage with a new terminal:
|
||||
|
|
|
@ -9,13 +9,13 @@ a logging library. If you have not yet installed Composer, refer to the
|
|||
> **Note:** for the sake of simplicity, this introduction will assume you
|
||||
> have performed a [local](00-intro.md#locally) install of Composer.
|
||||
|
||||
## `composer.json`: Project Setup
|
||||
## `composer.json`: Project setup
|
||||
|
||||
To start using Composer in your project, all you need is a `composer.json`
|
||||
file. This file describes the dependencies of your project and may contain
|
||||
other metadata as well.
|
||||
|
||||
### The `require` Key
|
||||
### The `require` key
|
||||
|
||||
The first (and often only) thing you specify in `composer.json` is the
|
||||
[`require`](04-schema.md#require) key. You are simply telling Composer which
|
||||
|
@ -41,10 +41,10 @@ assumed that the `monolog/monolog` package is registered on Packagist. (See more
|
|||
about Packagist [below](#packagist), or read more about repositories
|
||||
[here](05-repositories.md)).
|
||||
|
||||
### Package Names
|
||||
### Package names
|
||||
|
||||
The package name consists of a vendor name and the project's name. Often these
|
||||
will be identical - the vendor name just exists to prevent naming clashes. For
|
||||
will be identical - the vendor name only exists to prevent naming clashes. For
|
||||
example, it would allow two different people to create a library named `json`.
|
||||
One might be named `igorw/json` while the other might be `seldaek/json`.
|
||||
|
||||
|
@ -53,10 +53,10 @@ Read more about publishing packages and package naming [here](02-libraries.md).
|
|||
you to require certain versions of server software. See
|
||||
[platform packages](#platform-packages) below.)
|
||||
|
||||
### Package Version Constraints
|
||||
### Package version constraints
|
||||
|
||||
In our example, we are requesting the Monolog package with the version constraint
|
||||
[`1.0.*`](http://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*).
|
||||
[`1.0.*`](https://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*).
|
||||
This means any version in the `1.0` development branch, or any version that is
|
||||
greater than or equal to 1.0 and less than 1.1 (`>=1.0 <1.1`).
|
||||
|
||||
|
@ -84,9 +84,9 @@ versions, how versions relate to each other, and on version constraints.
|
|||
> versions of a package. Read more about stability flags and the `minimum-stability`
|
||||
> key on the [schema page](04-schema.md).
|
||||
|
||||
## Installing Dependencies
|
||||
## Installing dependencies
|
||||
|
||||
To install the defined dependencies for your project, just run the
|
||||
To install the defined dependencies for your project, run the
|
||||
[`install`](03-cli.md#install) command.
|
||||
|
||||
```sh
|
||||
|
@ -95,7 +95,7 @@ php composer.phar install
|
|||
|
||||
When you run this command, one of two things may happen:
|
||||
|
||||
### Installing Without `composer.lock`
|
||||
### Installing without `composer.lock`
|
||||
|
||||
If you have never run the command before and there is also no `composer.lock` file present,
|
||||
Composer simply resolves all dependencies listed in your `composer.json` file and downloads
|
||||
|
@ -114,7 +114,7 @@ of them that it downloaded to the `composer.lock` file, locking the project to t
|
|||
versions. You should commit the `composer.lock` file to your project repo so that all people
|
||||
working on the project are locked to the same versions of dependencies (more below).
|
||||
|
||||
### Installing With `composer.lock`
|
||||
### Installing with `composer.lock`
|
||||
|
||||
This brings us to the second scenario. If there is already a `composer.lock` file as well as a
|
||||
`composer.json` file when you run `composer install`, it means either you ran the
|
||||
|
@ -130,7 +130,7 @@ working on your project. As a result you will have all dependencies requested by
|
|||
the file was created). This is by design, it ensures that your project does not break because of
|
||||
unexpected changes in dependencies.
|
||||
|
||||
### Commit Your `composer.lock` File to Version Control
|
||||
### Commit your `composer.lock` file to version control
|
||||
|
||||
Committing this file to VC is important because it will cause anyone who sets
|
||||
up the project to use the exact same
|
||||
|
@ -142,7 +142,7 @@ reinstalling the project you can feel confident the dependencies installed are
|
|||
still working even if your dependencies released many new versions since then.
|
||||
(See note below about using the `update` command.)
|
||||
|
||||
## Updating Dependencies to their Latest Versions
|
||||
## Updating dependencies to their latest versions
|
||||
|
||||
As mentioned above, the `composer.lock` file prevents you from automatically getting
|
||||
the latest versions of your dependencies. To update to the latest versions, use the
|
||||
|
@ -154,8 +154,10 @@ and running `install` again.)
|
|||
```sh
|
||||
php composer.phar update
|
||||
```
|
||||
|
||||
> **Note:** Composer will display a Warning when executing an `install` command
|
||||
> if `composer.lock` and `composer.json` are not synchronized.
|
||||
> if the `composer.lock` has not been updated since changes were made to the
|
||||
> `composer.json` that might affect dependency resolution.
|
||||
|
||||
If you only want to install or update one dependency, you can whitelist them:
|
||||
|
||||
|
@ -188,15 +190,15 @@ installed on the system but are not actually installable by Composer. This
|
|||
includes PHP itself, PHP extensions and some system libraries.
|
||||
|
||||
* `php` represents the PHP version of the user, allowing you to apply
|
||||
constraints, e.g. `>=5.4.0`. To require a 64bit version of php, you can
|
||||
constraints, e.g. `^7.1`. To require a 64bit version of php, you can
|
||||
require the `php-64bit` package.
|
||||
|
||||
* `hhvm` represents the version of the HHVM runtime and allows you to apply
|
||||
a constraint, e.g., '>=2.3.3'.
|
||||
a constraint, e.g., `^2.3`.
|
||||
|
||||
* `ext-<name>` allows you to require PHP extensions (includes core
|
||||
extensions). Versioning can be quite inconsistent here, so it's often
|
||||
a good idea to just set the constraint to `*`. An example of an extension
|
||||
a good idea to set the constraint to `*`. An example of an extension
|
||||
package name is `ext-gd`.
|
||||
|
||||
* `lib-<name>` allows constraints to be made on versions of libraries used by
|
||||
|
@ -255,10 +257,10 @@ In addition to PSR-4 autoloading, Composer also supports PSR-0, classmap and
|
|||
files autoloading. See the [`autoload`](04-schema.md#autoload) reference for
|
||||
more information.
|
||||
|
||||
See also the docs on [`optimizing the autoloader`](articles/autoloader-optimization.md).
|
||||
See also the docs on [optimizing the autoloader](articles/autoloader-optimization.md).
|
||||
|
||||
> **Note:** Composer provides its own autoloader. If you don't want to use that
|
||||
> one, you can just include `vendor/composer/autoload_*.php` files, which return
|
||||
> one, you can include `vendor/composer/autoload_*.php` files, which return
|
||||
> associative arrays allowing you to configure your own autoloader.
|
||||
|
||||
← [Intro](00-intro.md) | [Libraries](02-libraries.md) →
|
||||
|
|
222
doc/03-cli.md
222
doc/03-cli.md
|
@ -22,6 +22,8 @@ The following options are available with every command:
|
|||
* **--quiet (-q):** Do not output any message.
|
||||
* **--no-interaction (-n):** Do not ask any interactive question.
|
||||
* **--no-plugins:** Disables plugins.
|
||||
* **--no-cache:** Disables the use of the cache directory. Same as setting the COMPOSER_CACHE_DIR
|
||||
env var to /dev/null (or NUL on Windows).
|
||||
* **--working-dir (-d):** If specified, use the given directory as working directory.
|
||||
* **--profile:** Display timing and memory usage information
|
||||
* **--ansi:** Force ANSI output.
|
||||
|
@ -65,7 +67,7 @@ php composer.phar init
|
|||
to a `composer` repository or a JSON string which similar to what the
|
||||
[repositories](04-schema.md#repositories) key accepts.
|
||||
|
||||
## install
|
||||
## install / i
|
||||
|
||||
The `install` command reads the `composer.json` file from the current
|
||||
directory, resolves the dependencies, and installs them into `vendor`.
|
||||
|
@ -115,7 +117,7 @@ resolution.
|
|||
requirements and force the installation even if the local machine does not
|
||||
fulfill these. See also the [`platform`](06-config.md#platform) config option.
|
||||
|
||||
## update
|
||||
## update / u
|
||||
|
||||
In order to get the latest versions of the dependencies and to update the
|
||||
`composer.lock` file, you should use the `update` command. This command is also
|
||||
|
@ -129,7 +131,7 @@ php composer.phar update
|
|||
This will resolve all dependencies of the project and write the exact versions
|
||||
into `composer.lock`.
|
||||
|
||||
If you just want to update a few packages and not all, you can list them as such:
|
||||
If you only want to update a few packages and not all, you can list them as such:
|
||||
|
||||
```sh
|
||||
php composer.phar update vendor/package vendor/package2
|
||||
|
@ -138,7 +140,7 @@ php composer.phar update vendor/package vendor/package2
|
|||
You can also use wildcards to update a bunch of packages at once:
|
||||
|
||||
```sh
|
||||
php composer.phar update vendor/*
|
||||
php composer.phar update "vendor/*"
|
||||
```
|
||||
|
||||
### Options
|
||||
|
@ -184,7 +186,7 @@ php composer.phar require
|
|||
After adding/changing the requirements, the modified requirements will be
|
||||
installed or updated.
|
||||
|
||||
If you do not want to choose requirements interactively, you can just pass them
|
||||
If you do not want to choose requirements interactively, you can pass them
|
||||
to the command.
|
||||
|
||||
```sh
|
||||
|
@ -220,7 +222,6 @@ If you do not specify a package, composer will prompt you to search for a packag
|
|||
Implicitly enables `--optimize-autoloader`.
|
||||
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
|
||||
|
||||
|
||||
## remove
|
||||
|
||||
The `remove` command removes packages from the `composer.json` file from
|
||||
|
@ -258,6 +259,10 @@ match the platform requirements of the installed packages. This can be used
|
|||
to verify that a production server has all the extensions needed to run a
|
||||
project after installing it for example.
|
||||
|
||||
Unlike update/install, this command will ignore config.platform settings and
|
||||
check the real platform packages so you can be certain you have the required
|
||||
platform dependencies.
|
||||
|
||||
## global
|
||||
|
||||
The global command allows you to run other commands like `install`, `remove`, `require`
|
||||
|
@ -273,7 +278,7 @@ This can be used to install CLI utilities globally. Here is an example:
|
|||
php composer.phar global require friendsofphp/php-cs-fixer
|
||||
```
|
||||
|
||||
Now the `php-cs-fixer` binary is available globally. Just make sure your global
|
||||
Now the `php-cs-fixer` binary is available globally. Make sure your global
|
||||
[vendor binaries](articles/vendor-binaries.md) directory is in your `$PATH`
|
||||
environment variable, you can get its location with the following command :
|
||||
|
||||
|
@ -281,7 +286,7 @@ environment variable, you can get its location with the following command :
|
|||
php composer.phar global config bin-dir --absolute
|
||||
```
|
||||
|
||||
If you wish to update the binary later on you can just run a global update:
|
||||
If you wish to update the binary later on you can run a global update:
|
||||
|
||||
```sh
|
||||
php composer.phar global update
|
||||
|
@ -290,7 +295,7 @@ php composer.phar global update
|
|||
## search
|
||||
|
||||
The search command allows you to search through the current project's package
|
||||
repositories. Usually this will be just packagist. You simply pass it the
|
||||
repositories. Usually this will be packagist. You simply pass it the
|
||||
terms you want to search for.
|
||||
|
||||
```sh
|
||||
|
@ -492,7 +497,7 @@ php composer.phar validate
|
|||
|
||||
### Options
|
||||
|
||||
* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound version constraints.
|
||||
* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound or overly strict version constraints.
|
||||
* **--no-check-lock:** Do not emit an error if `composer.lock` exists and is not up to date.
|
||||
* **--no-check-publish:** Do not emit an error if `composer.json` is unsuitable for publishing as a package on Packagist but is otherwise valid.
|
||||
* **--with-dependencies:** Also validate the composer.json of all installed dependencies.
|
||||
|
@ -521,7 +526,7 @@ vendor/seld/jsonlint:
|
|||
|
||||
## self-update (selfupdate)
|
||||
|
||||
To update Composer itself to the latest version, just run the `self-update`
|
||||
To update Composer itself to the latest version, run the `self-update`
|
||||
command. It will replace your `composer.phar` with the latest version.
|
||||
|
||||
```sh
|
||||
|
@ -625,7 +630,7 @@ would set `"extra": { "foo": { "bar": "value" } }`.
|
|||
## create-project
|
||||
|
||||
You can use Composer to create new projects from an existing package. This is
|
||||
the equivalent of doing a git clone/svn checkout followed by a "composer install"
|
||||
the equivalent of doing a git clone/svn checkout followed by a `composer install`
|
||||
of the vendors.
|
||||
|
||||
There are several applications for this:
|
||||
|
@ -635,7 +640,7 @@ There are several applications for this:
|
|||
3. Projects with multiple developers can use this feature to bootstrap the
|
||||
initial application for development.
|
||||
|
||||
To create a new project using Composer you can use the "create-project" command.
|
||||
To create a new project using Composer you can use the `create-project` command.
|
||||
Pass it a package name, and the directory to create the project in. You can also
|
||||
provide a version as third argument, otherwise the latest version is used.
|
||||
|
||||
|
@ -666,9 +671,13 @@ By default the command checks for the packages on packagist.org.
|
|||
package.
|
||||
* **--no-progress:** Removes the progress display that can mess with some
|
||||
terminals or scripts which don't handle backspace characters.
|
||||
* **--no-secure-http:** Disable the secure-http config option temporarily while
|
||||
installing the root package. Use at your own risk. Using this flag is a bad
|
||||
idea.
|
||||
* **--keep-vcs:** Skip the deletion of the VCS metadata for the created
|
||||
project. This is mostly useful if you run the command in non-interactive
|
||||
mode.
|
||||
* **--remove-vcs:** Force-remove the VCS metadata without prompting.
|
||||
* **--no-install:** Disables installation of the vendors.
|
||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||
requirements and force the installation even if the local machine does not
|
||||
|
@ -677,7 +686,7 @@ By default the command checks for the packages on packagist.org.
|
|||
## dump-autoload (dumpautoload)
|
||||
|
||||
If you need to update the autoloader because of new classes in a classmap
|
||||
package for example, you can use "dump-autoload" to do that without having to
|
||||
package for example, you can use `dump-autoload` to do that without having to
|
||||
go through an install or update.
|
||||
|
||||
Additionally, it can dump an optimized autoloader that converts PSR-0/4 packages
|
||||
|
@ -688,7 +697,7 @@ using this option you can still use PSR-0/4 for convenience and classmaps for
|
|||
performance.
|
||||
|
||||
### Options
|
||||
* **--no-scripts:** Skips the execution of all scripts defined in composer.json file.
|
||||
* **--no-scripts:** Skips the execution of all scripts defined in `composer.json` file.
|
||||
* **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||
autoloader. This is recommended especially for production, but can take
|
||||
a bit of time to run so it is currently not done by default.
|
||||
|
@ -721,7 +730,7 @@ Lists the name, version and license of every package installed. Use
|
|||
* **--list (-l):** List user defined scripts.
|
||||
|
||||
To run [scripts](articles/scripts.md) manually you can use this command,
|
||||
just give it the script name and optionally any required arguments.
|
||||
give it the script name and optionally any required arguments.
|
||||
|
||||
## exec
|
||||
|
||||
|
@ -762,7 +771,7 @@ php composer.phar archive vendor/package 2.0.21 --format=zip
|
|||
|
||||
## help
|
||||
|
||||
To get more information about a certain command, just use `help`.
|
||||
To get more information about a certain command, you can use `help`.
|
||||
|
||||
```sh
|
||||
php composer.phar help install
|
||||
|
@ -793,6 +802,90 @@ COMPOSER=composer-other.json php composer.phar install
|
|||
|
||||
The generated lock file will use the same name: `composer-other.lock` in this example.
|
||||
|
||||
### COMPOSER_ALLOW_SUPERUSER
|
||||
|
||||
If set to 1, this env disables the warning about running commands as root/super user.
|
||||
It also disables automatic clearing of sudo sessions, so you should really only set this
|
||||
if you use Composer as super user at all times like in docker containers.
|
||||
|
||||
### COMPOSER_AUTH
|
||||
|
||||
The `COMPOSER_AUTH` var allows you to set up authentication as an environment variable.
|
||||
The contents of the variable should be a JSON formatted object containing http-basic,
|
||||
github-oauth, bitbucket-oauth, ... objects as needed, and following the
|
||||
[spec from the config](06-config.md#gitlab-oauth).
|
||||
|
||||
### COMPOSER_BIN_DIR
|
||||
|
||||
By setting this option you can change the `bin` ([Vendor Binaries](articles/vendor-binaries.md))
|
||||
directory to something other than `vendor/bin`.
|
||||
|
||||
### COMPOSER_CACHE_DIR
|
||||
|
||||
The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory,
|
||||
which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option.
|
||||
|
||||
By default it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and
|
||||
`C:\Users\<user>\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows.
|
||||
|
||||
### COMPOSER_CAFILE
|
||||
|
||||
By setting this environmental value, you can set a path to a certificate bundle
|
||||
file to be used during SSL/TLS peer verification.
|
||||
|
||||
### COMPOSER_DISCARD_CHANGES
|
||||
|
||||
This env var controls the [`discard-changes`](06-config.md#discard-changes) config option.
|
||||
|
||||
### COMPOSER_HOME
|
||||
|
||||
The `COMPOSER_HOME` var allows you to change the Composer home directory. This
|
||||
is a hidden, global (per-user on the machine) directory that is shared between
|
||||
all projects.
|
||||
|
||||
By default it points to `C:\Users\<user>\AppData\Roaming\Composer` on Windows
|
||||
and `/Users/<user>/.composer` on macOS. On \*nix systems that follow the [XDG Base
|
||||
Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html),
|
||||
it points to `$XDG_CONFIG_HOME/composer`. On other \*nix systems, it points to
|
||||
`/home/<user>/.composer`.
|
||||
|
||||
#### COMPOSER_HOME/config.json
|
||||
|
||||
You may put a `config.json` file into the location which `COMPOSER_HOME` points
|
||||
to. Composer will merge this configuration with your project's `composer.json`
|
||||
when you run the `install` and `update` commands.
|
||||
|
||||
This file allows you to set [repositories](05-repositories.md) and
|
||||
[configuration](06-config.md) for the user's projects.
|
||||
|
||||
In case global configuration matches _local_ configuration, the _local_
|
||||
configuration in the project's `composer.json` always wins.
|
||||
|
||||
### COMPOSER_HTACCESS_PROTECT
|
||||
|
||||
Defaults to `1`. If set to `0`, Composer will not create `.htaccess` files in the
|
||||
composer home, cache, and data directories.
|
||||
|
||||
### COMPOSER_MEMORY_LIMIT
|
||||
|
||||
If set, the value is used as php's memory_limit.
|
||||
|
||||
### COMPOSER_MIRROR_PATH_REPOS
|
||||
|
||||
If set to 1, this env changes the default path repository strategy to `mirror` instead
|
||||
of `symlink`. As it is the default strategy being set it can still be overwritten by
|
||||
repository options.
|
||||
|
||||
### COMPOSER_NO_INTERACTION
|
||||
|
||||
If set to 1, this env var will make Composer behave as if you passed the
|
||||
`--no-interaction` flag to every command. This can be set on build boxes/CI.
|
||||
|
||||
### COMPOSER_PROCESS_TIMEOUT
|
||||
|
||||
This env var controls the time Composer waits for commands (such as git
|
||||
commands) to finish executing. The default value is 300 seconds (5 minutes).
|
||||
|
||||
### COMPOSER_ROOT_VERSION
|
||||
|
||||
By setting this var you can specify the version of the root package, if it can
|
||||
|
@ -803,11 +896,6 @@ not be guessed from VCS info and is not present in `composer.json`.
|
|||
By setting this var you can make Composer install the dependencies into a
|
||||
directory other than `vendor`.
|
||||
|
||||
### COMPOSER_BIN_DIR
|
||||
|
||||
By setting this option you can change the `bin` ([Vendor Binaries](articles/vendor-binaries.md))
|
||||
directory to something other than `vendor/bin`.
|
||||
|
||||
### http_proxy or HTTP_PROXY
|
||||
|
||||
If you are using Composer from behind an HTTP proxy, you can use the standard
|
||||
|
@ -824,16 +912,6 @@ similar use case), and need to support proxies, please provide the `CGI_HTTP_PRO
|
|||
environment variable instead. See [httpoxy.org](https://httpoxy.org/) for further
|
||||
details.
|
||||
|
||||
### no_proxy or NO_PROXY
|
||||
|
||||
If you are behind a proxy and would like to disable it for certain domains, you
|
||||
can use the `no_proxy` or `NO_PROXY` env var. Simply set it to a comma separated list of
|
||||
domains the proxy should *not* be used for.
|
||||
|
||||
The env var accepts domains, IP addresses, and IP address blocks in CIDR
|
||||
notation. You can restrict the filter to a particular port (e.g. `:80`). You
|
||||
can also set it to `*` to ignore the proxy for all HTTP requests.
|
||||
|
||||
### HTTP_PROXY_REQUEST_FULLURI
|
||||
|
||||
If you use a proxy but it does not support the request_fulluri flag, then you
|
||||
|
@ -846,79 +924,23 @@ If you use a proxy but it does not support the request_fulluri flag for HTTPS
|
|||
requests, then you should set this env var to `false` or `0` to prevent Composer
|
||||
from setting the request_fulluri option.
|
||||
|
||||
### COMPOSER_HOME
|
||||
### COMPOSER_SELF_UPDATE_TARGET
|
||||
|
||||
The `COMPOSER_HOME` var allows you to change the Composer home directory. This
|
||||
is a hidden, global (per-user on the machine) directory that is shared between
|
||||
all projects.
|
||||
If set, makes the self-update command write the new Composer phar file into that path instead of overwriting itself. Useful for updating Composer on read-only filesystem.
|
||||
|
||||
By default it points to `C:\Users\<user>\AppData\Roaming\Composer` on Windows
|
||||
and `/Users/<user>/.composer` on OSX. On *nix systems that follow the [XDG Base
|
||||
Directory Specifications](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html),
|
||||
it points to `$XDG_CONFIG_HOME/composer`. On other *nix systems, it points to
|
||||
`/home/<user>/.composer`.
|
||||
### no_proxy or NO_PROXY
|
||||
|
||||
#### COMPOSER_HOME/config.json
|
||||
If you are behind a proxy and would like to disable it for certain domains, you
|
||||
can use the `no_proxy` or `NO_PROXY` env var. Simply set it to a comma separated list of
|
||||
domains the proxy should *not* be used for.
|
||||
|
||||
You may put a `config.json` file into the location which `COMPOSER_HOME` points
|
||||
to. Composer will merge this configuration with your project's `composer.json`
|
||||
when you run the `install` and `update` commands.
|
||||
The env var accepts domains, IP addresses, and IP address blocks in CIDR
|
||||
notation. You can restrict the filter to a particular port (e.g. `:80`). You
|
||||
can also set it to `*` to ignore the proxy for all HTTP requests.
|
||||
|
||||
This file allows you to set [repositories](05-repositories.md) and
|
||||
[configuration](06-config.md) for the user's projects.
|
||||
### COMPOSER_DISABLE_NETWORK
|
||||
|
||||
In case global configuration matches _local_ configuration, the _local_
|
||||
configuration in the project's `composer.json` always wins.
|
||||
|
||||
### COMPOSER_CACHE_DIR
|
||||
|
||||
The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory,
|
||||
which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option.
|
||||
|
||||
By default it points to `$COMPOSER_HOME/cache` on \*nix and OSX, and
|
||||
`C:\Users\<user>\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows.
|
||||
|
||||
### COMPOSER_PROCESS_TIMEOUT
|
||||
|
||||
This env var controls the time Composer waits for commands (such as git
|
||||
commands) to finish executing. The default value is 300 seconds (5 minutes).
|
||||
|
||||
### COMPOSER_CAFILE
|
||||
|
||||
By setting this environmental value, you can set a path to a certificate bundle
|
||||
file to be used during SSL/TLS peer verification.
|
||||
|
||||
### COMPOSER_AUTH
|
||||
|
||||
The `COMPOSER_AUTH` var allows you to set up authentication as an environment variable.
|
||||
The contents of the variable should be a JSON formatted object containing http-basic,
|
||||
github-oauth, bitbucket-oauth, ... objects as needed, and following the
|
||||
[spec from the config](06-config.md#gitlab-oauth).
|
||||
|
||||
### COMPOSER_DISCARD_CHANGES
|
||||
|
||||
This env var controls the [`discard-changes`](06-config.md#discard-changes) config option.
|
||||
|
||||
### COMPOSER_NO_INTERACTION
|
||||
|
||||
If set to 1, this env var will make Composer behave as if you passed the
|
||||
`--no-interaction` flag to every command. This can be set on build boxes/CI.
|
||||
|
||||
### COMPOSER_ALLOW_SUPERUSER
|
||||
|
||||
If set to 1, this env disables the warning about running commands as root/super user.
|
||||
It also disables automatic clearing of sudo sessions, so you should really only set this
|
||||
if you use Composer as super user at all times like in docker containers.
|
||||
|
||||
### COMPOSER_MIRROR_PATH_REPOS
|
||||
|
||||
If set to 1, this env changes the default path repository strategy to `mirror` instead
|
||||
of `symlink`. As it is the default strategy being set it can still be overwritten by
|
||||
repository options.
|
||||
|
||||
### COMPOSER_HTACCESS_PROTECT
|
||||
|
||||
Defaults to `1`. If set to `0`, Composer will not create `.htaccess` files in the
|
||||
composer home, cache, and data directories.
|
||||
If set to `1`, disables network access (best effort). This can be used for debugging or
|
||||
to run Composer on a plane or a starship with poor connectivity.
|
||||
|
||||
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
||||
|
|
|
@ -43,7 +43,7 @@ Required for published packages (libraries).
|
|||
|
||||
### description
|
||||
|
||||
A short description of the package. Usually this is just one line long.
|
||||
A short description of the package. Usually this is one line long.
|
||||
|
||||
Required for published packages (libraries).
|
||||
|
||||
|
@ -104,7 +104,7 @@ Out of the box, Composer supports four types:
|
|||
[dedicated article](articles/custom-installers.md).
|
||||
|
||||
Only use a custom type if you need custom logic during installation. It is
|
||||
recommended to omit this field and have it just default to `library`.
|
||||
recommended to omit this field and have it default to `library`.
|
||||
|
||||
### keywords
|
||||
|
||||
|
@ -127,6 +127,12 @@ An URL to the website of the project.
|
|||
|
||||
Optional.
|
||||
|
||||
### readme
|
||||
|
||||
A relative path to the readme document.
|
||||
|
||||
Optional.
|
||||
|
||||
### time
|
||||
|
||||
Release date of the version.
|
||||
|
@ -145,14 +151,14 @@ The recommended notation for the most common licenses is (alphabetical):
|
|||
- BSD-2-Clause
|
||||
- BSD-3-Clause
|
||||
- BSD-4-Clause
|
||||
- GPL-2.0
|
||||
- GPL-3.0
|
||||
- LGPL-2.1
|
||||
- LGPL-3.0
|
||||
- GPL-2.0-only / GPL-2.0-or-later
|
||||
- GPL-3.0-only / GPL-3.0-or-later
|
||||
- LGPL-2.1-only / LGPL-2.1-or-later
|
||||
- LGPL-3.0-only / LGPL-3.0-or-later
|
||||
- MIT
|
||||
|
||||
Optional, but it is highly recommended to supply this. More identifiers are
|
||||
listed at the [SPDX Open Source License Registry](https://www.spdx.org/licenses/).
|
||||
listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/).
|
||||
|
||||
For closed-source software, you may use `"proprietary"` as the license identifier.
|
||||
|
||||
|
@ -172,8 +178,8 @@ An Example for disjunctive licenses:
|
|||
```json
|
||||
{
|
||||
"license": [
|
||||
"LGPL-2.1",
|
||||
"GPL-3.0+"
|
||||
"LGPL-2.1-only",
|
||||
"GPL-3.0-or-later"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
@ -182,7 +188,7 @@ Alternatively they can be separated with "or" and enclosed in parenthesis;
|
|||
|
||||
```json
|
||||
{
|
||||
"license": "(LGPL-2.1 or GPL-3.0+)"
|
||||
"license": "(LGPL-2.1-only or GPL-3.0-or-later)"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -214,7 +220,7 @@ An example:
|
|||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be",
|
||||
"homepage": "https://seld.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
]
|
||||
|
@ -237,6 +243,7 @@ Support information includes the following:
|
|||
* **source:** URL to browse or download the sources.
|
||||
* **docs:** URL to the documentation.
|
||||
* **rss:** URL to the RSS feed.
|
||||
* **chat:** URL to the chat channel.
|
||||
|
||||
An example:
|
||||
|
||||
|
@ -272,7 +279,7 @@ All links are optional fields.
|
|||
`require` and `require-dev` additionally support stability flags ([root-only](04-schema.md#root-package)).
|
||||
These allow you to further restrict or expand the stability of a package beyond
|
||||
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
|
||||
them to a constraint, or just apply them to an empty constraint if you want to
|
||||
them to a constraint, or apply them to an empty constraint if you want to
|
||||
allow unstable packages of a dependency for example.
|
||||
|
||||
Example:
|
||||
|
@ -307,7 +314,9 @@ releases for the `doctrine/data-fixtures` package :
|
|||
`require` and `require-dev` additionally support explicit references (i.e.
|
||||
commit) for dev versions to make sure they are locked to a given state, even
|
||||
when you run update. These only work if you explicitly require a dev version
|
||||
and append the reference with `#<ref>`.
|
||||
and append the reference with `#<ref>`. This is also a
|
||||
[root-only](04-schema.md#root-package) feature and will be ignored in
|
||||
dependencies.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -356,7 +365,6 @@ Example:
|
|||
> use and require. Alternatively you may use third party tools to analyze
|
||||
> your project for the list of extensions used.
|
||||
|
||||
|
||||
#### require
|
||||
|
||||
Lists packages required by this package. The package will not be installed
|
||||
|
@ -407,7 +415,7 @@ simply list it in `provide`.
|
|||
#### suggest
|
||||
|
||||
Suggested packages that can enhance or work well with this package. These are
|
||||
just informational and are displayed after the package is installed, to give
|
||||
informational and are displayed after the package is installed, to give
|
||||
your users a hint that they could add more packages, even though they are not
|
||||
strictly required.
|
||||
|
||||
|
@ -571,7 +579,7 @@ Example:
|
|||
#### Files
|
||||
|
||||
If you want to require certain files explicitly on every request then you can use
|
||||
the 'files' autoloading mechanism. This is useful if your package includes PHP functions
|
||||
the `files` autoloading mechanism. This is useful if your package includes PHP functions
|
||||
that cannot be autoloaded by PHP.
|
||||
|
||||
Example:
|
||||
|
@ -586,7 +594,7 @@ Example:
|
|||
|
||||
#### Exclude files from classmaps
|
||||
|
||||
If you want to exclude some files or folders from the classmap you can use the 'exclude-from-classmap' property.
|
||||
If you want to exclude some files or folders from the classmap you can use the `exclude-from-classmap` property.
|
||||
This might be useful to exclude test classes in your live environment, for example, as those will be skipped
|
||||
from the classmap even when building an optimized autoloader.
|
||||
|
||||
|
@ -608,7 +616,7 @@ Example:
|
|||
|
||||
The autoloader can have quite a substantial impact on your request time
|
||||
(50-100ms per request in large frameworks using a lot of classes). See the
|
||||
[`article about optimizing the autoloader`](articles/autoloader-optimization.md)
|
||||
[article about optimizing the autoloader](articles/autoloader-optimization.md)
|
||||
for more details on how to reduce this impact.
|
||||
|
||||
### autoload-dev <span>([root-only](04-schema.md#root-package))</span>
|
||||
|
@ -712,7 +720,7 @@ Use `"prefer-stable": true` to enable.
|
|||
|
||||
Custom package repositories to use.
|
||||
|
||||
By default Composer just uses the packagist repository. By specifying
|
||||
By default Composer only uses the packagist repository. By specifying
|
||||
repositories you can get packages from elsewhere.
|
||||
|
||||
Repositories are not resolved recursively. You can only add them to your main
|
||||
|
@ -732,7 +740,7 @@ The following repository types are supported:
|
|||
project.
|
||||
* **package:** If you depend on a project that does not have any support for
|
||||
composer whatsoever you can define the package inline using a `package`
|
||||
repository. You basically just inline the `composer.json` object.
|
||||
repository. You basically inline the `composer.json` object.
|
||||
|
||||
For more information on any of these, see [Repositories](05-repositories.md).
|
||||
|
||||
|
@ -768,7 +776,7 @@ Example:
|
|||
"name": "smarty/smarty",
|
||||
"version": "3.1.7",
|
||||
"dist": {
|
||||
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"type": "zip"
|
||||
},
|
||||
"source": {
|
||||
|
@ -862,6 +870,22 @@ The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`,
|
|||
|
||||
Optional.
|
||||
|
||||
### abandoned
|
||||
|
||||
Indicates whether this package has been abandoned.
|
||||
|
||||
It can be boolean or a package name/URL pointing to a recommended alternative.
|
||||
|
||||
Examples:
|
||||
|
||||
Use `"abandoned": true` to indicates this package is abandoned.
|
||||
Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned and the
|
||||
recommended alternative is `monolog/monolog`.
|
||||
|
||||
Defaults to false.
|
||||
|
||||
Optional.
|
||||
|
||||
### non-feature-branches
|
||||
|
||||
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something),
|
||||
|
@ -883,7 +907,7 @@ 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`.
|
||||
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:
|
||||
|
||||
|
@ -893,7 +917,7 @@ If you configure `latest-.*` as a pattern for non-feature-branches like this:
|
|||
}
|
||||
```
|
||||
|
||||
Then "composer show -s" will give you `versions : * dev-latest-testing`.
|
||||
Then `composer show -s` will give you `versions : * dev-latest-testing`.
|
||||
|
||||
Optional.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ understand some of the basic concepts that Composer is built on.
|
|||
### Package
|
||||
|
||||
Composer is a dependency manager. It installs packages locally. A package is
|
||||
essentially just a directory containing something. In this case it is PHP
|
||||
essentially a directory containing something. In this case it is PHP
|
||||
code, but in theory it could be anything. And it contains a package
|
||||
description which has a name and a version. The name and the version are used
|
||||
to identify the package.
|
||||
|
@ -57,9 +57,9 @@ The main repository type is the `composer` repository. It uses a single
|
|||
`packages.json` file that contains all of the package metadata.
|
||||
|
||||
This is also the repository type that packagist uses. To reference a
|
||||
`composer` repository, just supply the path before the `packages.json` file.
|
||||
`composer` repository, supply the path before the `packages.json` file.
|
||||
In the case of packagist, that file is located at `/packages.json`, so the URL of
|
||||
the repository would be `packagist.org`. For `example.org/packages.json` the
|
||||
the repository would be `repo.packagist.org`. For `example.org/packages.json` the
|
||||
repository URL would be `example.org`.
|
||||
|
||||
#### packages
|
||||
|
@ -93,7 +93,7 @@ Here is a minimal package definition:
|
|||
"name": "smarty/smarty",
|
||||
"version": "3.1.7",
|
||||
"dist": {
|
||||
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ integrity, for example:
|
|||
The file above declares that acme/foo and acme/bar can be found in this
|
||||
repository, by loading the file referenced by `providers-url`, replacing
|
||||
`%package%` by the vendor namespaced package name and `%hash%` by the
|
||||
sha256 field. Those files themselves just contain package definitions as
|
||||
sha256 field. Those files themselves contain package definitions as
|
||||
described [above](#packages).
|
||||
|
||||
These fields are optional. You probably don't need them for your own custom
|
||||
|
@ -284,8 +284,9 @@ VCS repository provides `dist`s for them that fetch the packages as zips.
|
|||
* **BitBucket:** [bitbucket.org](https://bitbucket.org) (Git and Mercurial)
|
||||
|
||||
The VCS driver to be used is detected automatically based on the URL. However,
|
||||
should you need to specify one for whatever reason, you can use `fossil`, `git`,
|
||||
`svn` or `hg` as the repository type instead of `vcs`.
|
||||
should you need to specify one for whatever reason, you can use `git-bitbucket`,
|
||||
`hg-bitbucket`, `github`, `gitlab`, `perforce`, `fossil`, `git`, `svn` or `hg`
|
||||
as the repository type instead of `vcs`.
|
||||
|
||||
If you set the `no-api` key to `true` on a github repository it will clone the
|
||||
repository as it would with any other git repository instead of using the
|
||||
|
@ -304,15 +305,12 @@ After creating an OAuth consumer in the BitBucket control panel, you need to set
|
|||
the credentials like this (more info [here](https://getcomposer.org/doc/06-config.md#bitbucket-oauth)):
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"bitbucket-oauth": {
|
||||
"bitbucket.org": {
|
||||
"consumer-key": "myKey",
|
||||
"consumer-secret": "mySecret"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
**Note that the repository endpoint needs to be https rather than git.**
|
||||
|
@ -488,7 +486,7 @@ Here is an example for the smarty template engine:
|
|||
"name": "smarty/smarty",
|
||||
"version": "3.1.7",
|
||||
"dist": {
|
||||
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||
"type": "zip"
|
||||
},
|
||||
"source": {
|
||||
|
@ -606,7 +604,7 @@ private packages:
|
|||
}
|
||||
```
|
||||
|
||||
Each zip artifact is just a ZIP archive with `composer.json` in root folder:
|
||||
Each zip artifact is a ZIP archive with `composer.json` in root folder:
|
||||
|
||||
```sh
|
||||
unzip -l acme-corp-parser-10.3.5.zip
|
||||
|
@ -659,7 +657,7 @@ be explicitly defined in the package's `composer.json` file. If the version
|
|||
cannot be resolved by these means, it is assumed to be `dev-master`.
|
||||
|
||||
The local package will be symlinked if possible, in which case the output in
|
||||
the console will read `Symlinked from ../../packages/my-package`. If symlinking
|
||||
the console will read `Symlinking from ../../packages/my-package`. If symlinking
|
||||
is _not_ possible the package will be copied. In that case, the console will
|
||||
output `Mirrored from ../../packages/my-package`.
|
||||
|
||||
|
@ -668,6 +666,10 @@ Instead of default fallback strategy you can force to use symlink with
|
|||
mirroring can be useful when deploying or generating package from a
|
||||
monolithic repository.
|
||||
|
||||
> **Note:** On Windows, directory symlinks are implemented using NTFS junctions
|
||||
> because they can be created by non-admin users. Mirroring will always be used
|
||||
> on versions below Windows 7 or if `proc_open` has been disabled.
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -708,7 +710,7 @@ You can disable the default Packagist.org repository by adding this to your
|
|||
|
||||
You can disable Packagist.org globally by using the global config flag:
|
||||
|
||||
```
|
||||
```bash
|
||||
composer config -g repo.packagist false
|
||||
```
|
||||
|
||||
|
|
|
@ -9,6 +9,20 @@ Defaults to `300`. The duration processes like git clones can run before
|
|||
Composer assumes they died out. You may need to make this higher if you have a
|
||||
slow connection or huge vendors.
|
||||
|
||||
To disable the process timeout on a custom command under `scripts`, a static
|
||||
helper is available:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"phpunit"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## use-include-path
|
||||
|
||||
Defaults to `false`. If `true`, the Composer autoloader will also look for classes
|
||||
|
@ -65,13 +79,17 @@ an OAuth token for GitHub.
|
|||
|
||||
A list of domain names and oauth keys. For example using `{"gitlab.com":
|
||||
"oauthtoken"}` as the value of this option will use `oauthtoken` to access
|
||||
private repositories on gitlab.
|
||||
private repositories on gitlab. Please note: If the package is not hosted at
|
||||
gitlab.com the domain names must be also specified with the
|
||||
[`gitlab-domains`](06-config.md#gitlab-domains) option.
|
||||
|
||||
## gitlab-token
|
||||
|
||||
A list of domain names and private tokens. For example using `{"gitlab.com":
|
||||
"privatetoken"}` as the value of this option will use `privatetoken` to access
|
||||
private repositories on gitlab.
|
||||
private repositories on gitlab. Please note: If the package is not hosted at
|
||||
gitlab.com the domain names must be also specified with the
|
||||
[`gitlab-domains`](06-config.md#gitlab-domains) option.
|
||||
|
||||
## disable-tls
|
||||
|
||||
|
@ -120,7 +138,7 @@ value of this option will let Composer authenticate against example.org.
|
|||
|
||||
Lets you fake platform packages (PHP and extensions) so that you can emulate a
|
||||
production env or define your target platform in the config. Example: `{"php":
|
||||
"5.4", "ext-something": "4.0"}`.
|
||||
"7.0.3", "ext-something": "4.0.3"}`.
|
||||
|
||||
## vendor-dir
|
||||
|
||||
|
@ -230,6 +248,14 @@ github API will have a date instead of the machine hostname.
|
|||
Defaults to `["gitlab.com"]`. A list of domains of GitLab servers.
|
||||
This is used if you use the `gitlab` repository type.
|
||||
|
||||
## use-github-api
|
||||
|
||||
Defaults to `true`. Similar to the `no-api` key on a specific repository,
|
||||
setting `use-github-api` to `false` will define the global behavior for all
|
||||
GitHub repositories to clone the repository as it would with any other git
|
||||
repository instead of using the GitHub API. But unlike using the `git`
|
||||
driver directly, Composer will still attempt to use GitHub's zip files.
|
||||
|
||||
## notify-on-install
|
||||
|
||||
Defaults to `true`. Composer allows repositories to define a notification URL,
|
||||
|
|
|
@ -7,7 +7,7 @@ contributing.
|
|||
|
||||
If you would like to contribute to Composer, please read the
|
||||
[README](https://github.com/composer/composer) and
|
||||
[CONTRIBUTING](https://github.com//composer/composer/blob/master/.github/CONTRIBUTING.md)
|
||||
[CONTRIBUTING](https://github.com/composer/composer/blob/master/.github/CONTRIBUTING.md)
|
||||
documents.
|
||||
|
||||
The most important guidelines are described as follows:
|
||||
|
|
|
@ -60,7 +60,7 @@ Branch aliases are great for aliasing main development lines. But in order to
|
|||
use them you need to have control over the source repository, and you need to
|
||||
commit changes to version control.
|
||||
|
||||
This is not really fun when you just want to try a bugfix of some library that
|
||||
This is not really fun when you want to try a bugfix of some library that
|
||||
is a dependency of your local project.
|
||||
|
||||
For this reason, you can alias packages in your `require` and `require-dev`
|
||||
|
@ -72,7 +72,7 @@ local project.
|
|||
You are using `symfony/monolog-bundle` which requires `monolog/monolog` version
|
||||
`1.*`. So you need your `dev-bugfix` to match that constraint.
|
||||
|
||||
Just add this to your project's root `composer.json`:
|
||||
Add this to your project's root `composer.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -89,16 +89,23 @@ Just add this to your project's root `composer.json`:
|
|||
}
|
||||
```
|
||||
|
||||
Or let composer add it for you with:
|
||||
|
||||
```
|
||||
php composer.phar require monolog/monolog:"dev-bugfix as 1.0.x-dev"
|
||||
```
|
||||
|
||||
That will fetch the `dev-bugfix` version of `monolog/monolog` from your GitHub
|
||||
and alias it to `1.0.x-dev`.
|
||||
|
||||
> **Note:** If a package with inline aliases is required, the alias (right of
|
||||
> the `as`) is used as the version constraint. The part left of the `as` is
|
||||
> discarded. As a consequence, if A requires B and B requires `monolog/monolog`
|
||||
> version `dev-bugfix as 1.0.x-dev`, installing A will make B require
|
||||
> `1.0.x-dev`, which may exist as a branch alias or an actual `1.0` branch. If
|
||||
> it does not, it must be re-inline-aliased in A's `composer.json`.
|
||||
> **Note:** Inline aliasing is a root-only feature. If a package with inline
|
||||
> aliases is required, the alias (right of the `as`) is used as the version
|
||||
> constraint. The part left of the `as` is discarded. As a consequence, if
|
||||
> A requires B and B requires `monolog/monolog` version `dev-bugfix as 1.0.x-dev`,
|
||||
> installing A will make B require `1.0.x-dev`, which may exist as a branch
|
||||
> alias or an actual `1.0` branch. If it does not, it must be
|
||||
> inline-aliased again in A's `composer.json`.
|
||||
|
||||
> **Note:** Inline aliasing should be avoided, especially for published
|
||||
> packages. If you found a bug, try and get your fix merged upstream. This
|
||||
> helps to avoid issues for users of your package.
|
||||
> packages/libraries. If you found a bug, try and get your fix merged upstream.
|
||||
> This helps to avoid issues for users of your package.
|
||||
|
|
|
@ -53,7 +53,6 @@ result in slow filesystem checks. To solve this issue two Level 2 optimization
|
|||
options exist, and you can decide to enable either if you have a lot of
|
||||
class_exists checks that are done for classes that do not exist in your project.
|
||||
|
||||
|
||||
## Optimization Level 2/A: Authoritative class maps
|
||||
|
||||
### How to run it?
|
||||
|
@ -82,7 +81,6 @@ then you might experience "class not found" issues in production. Enable this wi
|
|||
> Note: This can not be combined with Level 2/B optimizations. You have to choose one as
|
||||
> they address the same issue in different ways.
|
||||
|
||||
|
||||
## Optimization Level 2/B: APCu cache
|
||||
|
||||
### How to run it?
|
||||
|
|
|
@ -77,7 +77,7 @@ or another constraint if you want really specific versions.
|
|||
}
|
||||
```
|
||||
|
||||
Once you've done this, you just run:
|
||||
Once you've done this, you run:
|
||||
|
||||
php bin/satis build <configuration file> <build dir>
|
||||
|
||||
|
@ -112,6 +112,19 @@ Note that this will still need to pull and scan all of your VCS repositories
|
|||
because any VCS repository might contain (on any branch) one of the selected
|
||||
packages.
|
||||
|
||||
If you want to scan only the selected package and not all VCS repositories you need
|
||||
to declare a *name* for all your package (this only work on VCS repositories type) :
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
{ "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
|
||||
{ "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
||||
{ "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to scan only a single repository and update all packages found in
|
||||
it, pass the VCS repository URL as an optional argument:
|
||||
|
||||
|
@ -306,7 +319,7 @@ be marked abandoned as well.
|
|||
|
||||
It is possible to make satis automatically resolve and add all dependencies for
|
||||
your projects. This can be used with the Downloads functionality to have a
|
||||
complete local mirror of packages. Just add the following to your `satis.json`:
|
||||
complete local mirror of packages. Add the following to your `satis.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -336,9 +349,8 @@ is set to true.
|
|||
* `notify-batch`: optional, specify a URL that will be called every time a
|
||||
user installs a package. See [notify-batch].
|
||||
|
||||
|
||||
[ssh2 context options]: https://secure.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options
|
||||
[ssl context options]: https://secure.php.net/manual/en/context.ssl.php
|
||||
[Twig]: http://twig.sensiolabs.org/
|
||||
[Twig]: https://twig.sensiolabs.org/
|
||||
[config schema]: https://getcomposer.org/doc/04-schema.md#config
|
||||
[notify-batch]: https://getcomposer.org/doc/05-repositories.md#notify-batch
|
||||
|
|
|
@ -176,8 +176,8 @@ class AwsPlugin implements PluginInterface, EventSubscriberInterface
|
|||
|
||||
if ($protocol === 's3') {
|
||||
$awsClient = new AwsClient($this->io, $this->composer->getConfig());
|
||||
$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
|
||||
$event->setRemoteFilesystem($s3RemoteFilesystem);
|
||||
$s3Downloader = new S3Downloader($this->io, $event->getHttpDownloader()->getOptions(), $awsClient);
|
||||
$event->setHttpdownloader($s3Downloader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,6 +261,11 @@ Now the `custom-plugin-command` is available alongside Composer commands.
|
|||
|
||||
> _Composer commands are based on the [Symfony Console Component][10]._
|
||||
|
||||
## Running plugins manually
|
||||
|
||||
Plugins for an event can be run manually by the `run-script` command. This works the same way as
|
||||
[running scripts manually](scripts.md#running-scripts-manually).
|
||||
|
||||
## Using Plugins
|
||||
|
||||
Plugin packages are automatically loaded as soon as they are installed and will
|
||||
|
@ -282,4 +287,4 @@ local project plugins are loaded.
|
|||
[7]: ../01-basic-usage.md#package-versions
|
||||
[8]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capable.php
|
||||
[9]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capability/CommandProvider.php
|
||||
[10]: http://symfony.com/doc/current/components/console/introduction.html
|
||||
[10]: https://symfony.com/doc/current/components/console.html
|
||||
|
|
|
@ -15,7 +15,6 @@ the Composer execution process.
|
|||
> executed. If a dependency of the root package specifies its own scripts,
|
||||
> Composer does not execute those additional scripts.
|
||||
|
||||
|
||||
## Event names
|
||||
|
||||
Composer fires the following named events during its execution process:
|
||||
|
@ -62,8 +61,11 @@ Composer fires the following named events during its execution process:
|
|||
- **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
|
||||
you to manipulate the `HttpDownloader` object prior to downloading files
|
||||
based on the URL to be downloaded.
|
||||
- **pre-command-run**: occurs before a command is executed and allows you to
|
||||
manipulate the `InputInterface` object's options and arguments to tweak
|
||||
a command's behavior.
|
||||
|
||||
> **Note:** Composer makes no assumptions about the state of your dependencies
|
||||
> prior to `install` or `update`. Therefore, you should not specify scripts
|
||||
|
@ -187,7 +189,7 @@ composer run-script [--dev] [--no-dev] script
|
|||
```
|
||||
|
||||
For example `composer run-script post-install-cmd` will run any
|
||||
**post-install-cmd** scripts that have been defined.
|
||||
**post-install-cmd** scripts and [plugins](plugins.md) that have been defined.
|
||||
|
||||
You can also give additional arguments to the script handler by appending `--`
|
||||
followed by the handler arguments. e.g.
|
||||
|
@ -219,6 +221,56 @@ to the `phpunit` script.
|
|||
> are easily accessible. In this example no matter if the `phpunit` binary is
|
||||
> actually in `vendor/bin/phpunit` or `bin/phpunit` it will be found and executed.
|
||||
|
||||
Although Composer is not intended to manage long-running processes and other
|
||||
such aspects of PHP projects, it can sometimes be handy to disable the process
|
||||
timeout on custom commands. This timeout defaults to 300 seconds and can be
|
||||
overridden in a variety of ways depending on the desired effect:
|
||||
|
||||
- disable it for all commands using the config key `process-timeout`,
|
||||
- disable it for the current or future invocations of composer using the
|
||||
environment variable `COMPOSER_PROCESS_TIMEOUT`,
|
||||
- for a specific invocation using the `--timeout` flag of the `run-script` command,
|
||||
- using a static helper for specific scripts.
|
||||
|
||||
To disable the timeout for specific scripts with the static helper directly in
|
||||
composer.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"phpunit"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To disable the timeout for every script on a given project, you can use the
|
||||
composer.json configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"process-timeout": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's also possible to set the global environment variable to disable the timeout
|
||||
of all following scripts in the current terminal environment:
|
||||
|
||||
```
|
||||
export COMPOSER_PROCESS_TIMEOUT=0
|
||||
```
|
||||
|
||||
To disable the timeout of a single script call, you must use the `run-script` composer
|
||||
command and specify the `--timeout` parameter:
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 test
|
||||
```
|
||||
|
||||
## Referencing scripts
|
||||
|
||||
To enable script re-use and avoid duplicates, you can call a script from another
|
||||
|
@ -236,6 +288,17 @@ one by prefixing the command name with `@`:
|
|||
}
|
||||
```
|
||||
|
||||
You can also refer a script and pass it new arguments:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"tests": "phpunit",
|
||||
"testsVerbose": "@tests -vvv"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Calling Composer commands
|
||||
|
||||
To call Composer commands, you can use `@composer` which will automatically
|
||||
|
|
|
@ -140,13 +140,19 @@ Debian-like systems):
|
|||
memory_limit = -1
|
||||
```
|
||||
|
||||
Composer also respects a memory limit defined by the `COMPOSER_MEMORY_LIMIT` environment variable:
|
||||
|
||||
```sh
|
||||
COMPOSER_MEMORY_LIMIT=-1 composer.phar <...>
|
||||
```
|
||||
|
||||
Or, you can increase the limit with a command-line argument:
|
||||
|
||||
```sh
|
||||
php -d memory_limit=-1 composer.phar <...>
|
||||
```
|
||||
|
||||
This issue can also happen on cPanel instances, when the shell fork bomb protection is activated. For more information, see the [documentation](https://documentation.cpanel.net/display/ALD/Shell+Fork+Bomb+Protection) of the fork bomb feature on the cPanel site.
|
||||
This issue can also happen on cPanel instances, when the shell fork bomb protection is activated. For more information, see the [documentation](https://documentation.cpanel.net/display/68Docs/Shell+Fork+Bomb+Protection) of the fork bomb feature on the cPanel site.
|
||||
|
||||
## Xdebug impact on Composer
|
||||
|
||||
|
@ -163,7 +169,7 @@ please report this [issue](https://github.com/composer/composer/issues).
|
|||
2. Search for an `AutoRun` key inside `HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor`,
|
||||
`HKEY_CURRENT_USER\Software\Microsoft\Command Processor`
|
||||
or `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Command Processor`.
|
||||
3. Check if it contains any path to non-existent file, if it's the case, just remove them.
|
||||
3. Check if it contains any path to non-existent file, if it's the case, remove them.
|
||||
|
||||
## API rate limit and OAuth tokens
|
||||
|
||||
|
@ -244,7 +250,7 @@ following workarounds:
|
|||
On linux, it seems that running this command helps to make ipv4 traffic have a
|
||||
higher prio than ipv6, which is a better alternative than disabling ipv6 entirely:
|
||||
|
||||
```Bash
|
||||
```bash
|
||||
sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"
|
||||
```
|
||||
|
||||
|
@ -256,13 +262,13 @@ On windows the only way is to disable ipv6 entirely I am afraid (either in windo
|
|||
|
||||
Get name of your network device:
|
||||
|
||||
```
|
||||
```bash
|
||||
networksetup -listallnetworkservices
|
||||
```
|
||||
|
||||
Disable IPv6 on that device (in this case "Wi-Fi"):
|
||||
|
||||
```
|
||||
```bash
|
||||
networksetup -setv6off Wi-Fi
|
||||
```
|
||||
|
||||
|
@ -270,7 +276,7 @@ Run composer ...
|
|||
|
||||
You can enable IPv6 again with:
|
||||
|
||||
```
|
||||
```bash
|
||||
networksetup -setv6automatic Wi-Fi
|
||||
```
|
||||
|
||||
|
@ -281,14 +287,14 @@ for everyone.
|
|||
## Composer hangs with SSH ControlMaster
|
||||
|
||||
When you try to install packages from a Git repository and you use the `ControlMaster`
|
||||
setting for your SSH connection, Composer might just hang endlessly and you see a `sh`
|
||||
setting for your SSH connection, Composer might hang endlessly and you see a `sh`
|
||||
process in the `defunct` state in your process list.
|
||||
|
||||
The reason for this is a SSH Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1988
|
||||
|
||||
As a workaround, open a SSH connection to your Git host before running Composer:
|
||||
|
||||
```
|
||||
```bash
|
||||
ssh -t git@mygitserver.tld
|
||||
composer update
|
||||
```
|
||||
|
|
|
@ -13,7 +13,6 @@ If a package contains other scripts that are not needed by the package
|
|||
users (like build or compile scripts) that code should not be listed
|
||||
as a vendor binary.
|
||||
|
||||
|
||||
## How is it defined?
|
||||
|
||||
It is defined by adding the `bin` key to a project's `composer.json`.
|
||||
|
@ -34,12 +33,10 @@ for any project that **depends** on that project.
|
|||
This is a convenient way to expose useful scripts that would
|
||||
otherwise be hidden deep in the `vendor/` directory.
|
||||
|
||||
|
||||
## What happens when Composer is run on a composer.json that defines vendor binaries?
|
||||
|
||||
For the binaries that a package defines directly, nothing happens.
|
||||
|
||||
|
||||
## What happens when Composer is run on a composer.json that has dependencies with vendor binaries listed?
|
||||
|
||||
Composer looks for the binaries defined in all of the dependencies. A
|
||||
|
@ -69,13 +66,12 @@ Say project `my-vendor/project-b` has requirements setup like this:
|
|||
```
|
||||
|
||||
Running `composer install` for this `composer.json` will look at
|
||||
all of project-b's dependencies and install them to `vendor/bin`.
|
||||
all of project-a's binaries and install them to `vendor/bin`.
|
||||
|
||||
In this case, Composer will make `vendor/my-vendor/project-a/bin/project-a-bin`
|
||||
available as `vendor/bin/project-a-bin`. On a Unix-like platform
|
||||
this is accomplished by creating a symlink.
|
||||
|
||||
|
||||
## What about Windows and .bat files?
|
||||
|
||||
Packages managed entirely by Composer do not *need* to contain any
|
||||
|
@ -90,7 +86,6 @@ Packages that need to support workflows that may not include Composer
|
|||
are welcome to maintain custom `.bat` files. In this case, the package
|
||||
should **not** list the `.bat` file as a binary as it is not needed.
|
||||
|
||||
|
||||
## Can vendor binaries be installed somewhere other than vendor/bin?
|
||||
|
||||
Yes, there are two ways an alternate vendor binary location can be specified:
|
||||
|
|
|
@ -32,7 +32,7 @@ repository:*
|
|||
v1
|
||||
v2
|
||||
my-feature
|
||||
nother-feature
|
||||
another-feature
|
||||
|
||||
~/my-library$ git tag
|
||||
v1.0
|
||||
|
@ -74,11 +74,11 @@ correct location in your `vendor` directory.
|
|||
|
||||
### Branches
|
||||
|
||||
If you want Composer to check out a branch instead of a tag, you need to point it to the branch using the special `dev-*` prefix (or sometimes suffix; see below). If you're checking out a branch, it's assumed that you want to *work* on the branch and Composer actually clones the repo into the correct place in your `vendor` directory. For tags, it just copies the right files without actually cloning the repo. (You can modify this behavior with --prefer-source and --prefer-dist, see [install options](../03-cli.md#install).)
|
||||
If you want Composer to check out a branch instead of a tag, you need to point it to the branch using the special `dev-*` prefix (or sometimes suffix; see below). If you're checking out a branch, it's assumed that you want to *work* on the branch and Composer actually clones the repo into the correct place in your `vendor` directory. For tags, it copies the right files without actually cloning the repo. (You can modify this behavior with --prefer-source and --prefer-dist, see [install options](../03-cli.md#install).)
|
||||
|
||||
In the above example, if you wanted to check out the `my-feature` branch, you would specify `dev-my-feature` as the version constraint in your `require` clause. This would result in Composer cloning the `my-library` repository into my `vendor` directory and checking out the `my-feature` branch.
|
||||
|
||||
When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: `v1` and `v2`. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: `v1.x-dev`. The `.x` is an arbitrary string that Composer requires to tell it that we're talking about the `v1` branch and not a `v1` tag (alternatively, you can just name the branch `v1.x` instead of `v1`). In the case of a branch with a version-like name (`v1`, in this case), you append `-dev` as a suffix, rather than using `dev-` as a prefix.
|
||||
When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: `v1` and `v2`. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: `v1.x-dev`. The `.x` is an arbitrary string that Composer requires to tell it that we're talking about the `v1` branch and not a `v1` tag (alternatively, you can name the branch `v1.x` instead of `v1`). In the case of a branch with a version-like name (`v1`, in this case), you append `-dev` as a suffix, rather than using `dev-` as a prefix.
|
||||
|
||||
### Minimum Stability
|
||||
|
||||
|
@ -140,7 +140,7 @@ Example: `1.0.*`
|
|||
The `~` operator is best explained by example: `~1.2` is equivalent to
|
||||
`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
|
||||
it is mostly useful for projects respecting [semantic
|
||||
versioning](http://semver.org/). A common usage would be to mark the minimum
|
||||
versioning](https://semver.org/). A common usage would be to mark the minimum
|
||||
minor version you depend on, like `~1.2` (which allows anything up to, but not
|
||||
including, 2.0). Since in theory there should be no backwards compatibility
|
||||
breaks until 2.0, that works well. Another way of looking at it is that using
|
||||
|
@ -201,7 +201,7 @@ setting. All available stability flags are listed on the minimum-stability
|
|||
section of the [schema page](../04-schema.md#minimum-stability).
|
||||
|
||||
## Summary
|
||||
```
|
||||
```json
|
||||
"require": {
|
||||
"vendor/package": "1.3.2", // exactly 1.3.2
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ WordPress theme:
|
|||
|
||||
Now when your theme is installed with Composer it will be placed into
|
||||
`wp-content/themes/themename/` folder. Check the
|
||||
[current supported types](https://github.com/composer/installers#current-supported-types)
|
||||
[current supported types](https://github.com/composer/installers#current-supported-package-types)
|
||||
for your package.
|
||||
|
||||
As a **package consumer** you can set or override the install path for a package
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
As noted on the download page, the installer script contains a
|
||||
signature which changes when the installer code changes and as such
|
||||
it should not be relied upon long term.
|
||||
it should not be relied upon in the long term.
|
||||
|
||||
An alternative is to use this script which only works with unix utils:
|
||||
An alternative is to use this script which only works with UNIX utilities:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
|
||||
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
|
||||
EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)"
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
|
||||
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
|
||||
|
||||
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
|
||||
then
|
||||
|
@ -29,13 +29,13 @@ exit $RESULT
|
|||
The script will exit with 1 in case of failure, or 0 on success, and is quiet
|
||||
if no error occurs.
|
||||
|
||||
Alternatively if you want to rely on an exact copy of the installer you can fetch
|
||||
a specific version from github's history. The commit hash should be enough to
|
||||
Alternatively, if you want to rely on an exact copy of the installer, you can fetch
|
||||
a specific version from GitHub's history. The commit hash should be enough to
|
||||
give it uniqueness and authenticity as long as you can trust the GitHub servers.
|
||||
For example:
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/composer/getcomposer.org/1b137f8bf6db3e79a38a5bc45324414a6b1f9df2/web/installer -O - -q | php -- --quiet
|
||||
wget https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -O - -q | php -- --quiet
|
||||
```
|
||||
|
||||
You may replace the commit hash by whatever the last commit hash is on
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Which version numbering system does Composer itself use?
|
||||
|
||||
Composer uses [Semantic Versioning (aka SemVer)
|
||||
2.0.0](https://semver.org/spec/v2.0.0.html).
|
|
@ -14,7 +14,7 @@ compatible with the new major version of your dependency.
|
|||
|
||||
For example instead of using `>=3.4` you should use `~3.4` which allows all
|
||||
versions up to `3.999` but does not include `4.0` and above. The `^` operator
|
||||
works very well with libraries following [semantic versioning](http://semver.org).
|
||||
works very well with libraries following [semantic versioning](https://semver.org).
|
||||
|
||||
**Note:** As a package maintainer, you can make the life of your users easier
|
||||
by providing an [alias version](../articles/aliases.md) for your development
|
||||
|
|
|
@ -16,6 +16,6 @@ but it is not possible to determine if when you wrote that you were thinking
|
|||
of a package in version 3.0.0 or not. Should it match because you asked for
|
||||
`>=2` or should it not match because you asked for a `2.*`?
|
||||
|
||||
For this reason, Composer just throws an error and says that this is invalid.
|
||||
For this reason, Composer throws an error and says that this is invalid.
|
||||
The easy way to fix it is to think about what you really mean, and use only
|
||||
one of those rules.
|
|
@ -15,7 +15,7 @@ associated with inline VCS repositories.
|
|||
There are three ways the dependency solver could work with custom repositories:
|
||||
|
||||
- Fetch the repositories of root package, get all the packages from the defined
|
||||
repositories, resolve requirements. This is the current state and it works well
|
||||
repositories, then resolve requirements. This is the current state and it works well
|
||||
except for the limitation of not loading repositories recursively.
|
||||
|
||||
- Fetch the repositories of root package, while initializing packages from the
|
||||
|
|
|
@ -20,4 +20,3 @@ All these repositories contain the following packages.
|
|||
* `bar/baz` has a 1.0.0 version and 1.0.x-dev as well as dev-default branches.
|
||||
Additionally, 1.1.x-dev is a branch alias for dev-default.
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/../src/bootstrap.php';
|
|
@ -0,0 +1,42 @@
|
|||
parameters:
|
||||
level: 0
|
||||
excludes_analyse:
|
||||
- 'tests/Composer/Test/Fixtures'
|
||||
- 'tests/Composer/Test/Autoload/Fixtures'
|
||||
- 'tests/Composer/Test/Plugin/Fixtures'
|
||||
ignoreErrors:
|
||||
# unused parameters
|
||||
- '~^Constructor of class Composer\\Repository\\VcsRepository has an unused parameter \$dispatcher\.$~'
|
||||
- '~^Constructor of class Composer\\Repository\\PearRepository has an unused parameter \$dispatcher\.$~'
|
||||
- '~^Constructor of class Composer\\Util\\Http\\CurlDownloader has an unused parameter \$disableTls\.$~'
|
||||
- '~^Constructor of class Composer\\Util\\Http\\CurlDownloader has an unused parameter \$options\.$~'
|
||||
- '~^Constructor of class Composer\\Repository\\PearRepository has an unused parameter \$config\.$~'
|
||||
|
||||
# unused uses
|
||||
- '~^Anonymous function has an unused use \$io\.$~'
|
||||
- '~^Anonymous function has an unused use \$cache\.$~'
|
||||
- '~^Anonymous function has an unused use \$path\.$~'
|
||||
- '~^Anonymous function has an unused use \$fileName\.$~'
|
||||
|
||||
# ion cube is not installed
|
||||
- '~^Function ioncube_loader_\w+ not found\.$~'
|
||||
# rar is not installed
|
||||
- '~^Call to static method open\(\) on an unknown class RarArchive\.$~'
|
||||
# imagick is not installed
|
||||
- '~^Instantiated class Imagick not found\.$~'
|
||||
|
||||
# variables from global scope
|
||||
- '~^Undefined variable: \$vendorDir$~'
|
||||
- '~^Undefined variable: \$baseDir$~'
|
||||
|
||||
# variable defined in eval
|
||||
- '~^Undefined variable: \$res$~'
|
||||
|
||||
# always checked whether the class exists
|
||||
- '~^Instantiated class Symfony\\Component\\Console\\Terminal not found\.$~'
|
||||
- '~^Class Symfony\\Component\\Console\\Input\\StreamableInputInterface not found\.$~'
|
||||
- '~^Call to an undefined static method Symfony\\Component\\Process\\Process::fromShellCommandline\(\).$~'
|
||||
|
||||
# parent call in test mocks
|
||||
- '~^Composer\\Test\\Mock\\HttpDownloaderMock::__construct\(\) does not call parent constructor from Composer\\Util\\HttpDownloader\.$~'
|
||||
- '~^Composer\\Test\\Mock\\InstallationManagerMock::__construct\(\) does not call parent constructor from Composer\\Installer\\InstallationManager\.$~'
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
|
@ -8,7 +10,6 @@
|
|||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
"description": "Homepage URL for the project.",
|
||||
"format": "uri"
|
||||
},
|
||||
"readme": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the readme document."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes."
|
||||
|
@ -267,6 +271,10 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"use-github-api": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to true. If set to false, globally disables the use of the GitHub API for all GitHub repositories and clones the repository as it would for any other repository."
|
||||
},
|
||||
"archive-format": {
|
||||
"type": "string",
|
||||
"description": "The default archiving format when not provided on cli, defaults to \"tar\"."
|
||||
|
@ -278,6 +286,10 @@
|
|||
"htaccess-protect": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to true. If set to false, Composer will not create .htaccess files in the composer home, cache, and data directories."
|
||||
},
|
||||
"sort-packages": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false. If set to true, Composer will sort packages when adding/updating a new dependency."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -364,8 +376,8 @@
|
|||
"description": "If set to true, stable packages will be preferred to dev packages when possible, even if the minimum-stability allows unstable packages."
|
||||
},
|
||||
"bin": {
|
||||
"type": ["array"],
|
||||
"description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).",
|
||||
"type": ["string", "array"],
|
||||
"description": "A set of files, or a single file, that should be treated as binaries and symlinked into bin-dir (from config).",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -482,6 +494,11 @@
|
|||
"description": "IRC channel for support, as irc://server/channel.",
|
||||
"format": "uri"
|
||||
},
|
||||
"chat": {
|
||||
"type": "string",
|
||||
"description": "URL to the support chat.",
|
||||
"format": "uri"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "URL to browse or download the sources.",
|
||||
|
@ -670,15 +687,14 @@
|
|||
{ "$ref": "#/definitions/inline-package" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": { "$ref": "#/definitions/inline-package" }
|
||||
}
|
||||
"items": { "$ref": "#/definitions/inline-package" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"inline-package": {
|
||||
"type": "object",
|
||||
"required": ["name", "version"],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -772,8 +788,8 @@
|
|||
}
|
||||
},
|
||||
"bin": {
|
||||
"type": ["array"],
|
||||
"description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).",
|
||||
"type": ["string", "array"],
|
||||
"description": "A set of files, or a single file, that should be treated as binaries and symlinked into bin-dir (from config).",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use Composer\Package\PackageInterface;
|
|||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Script\ScriptEvents;
|
||||
use Composer\Util\PackageSorter;
|
||||
|
||||
/**
|
||||
* @author Igor Wiedler <igor@wiedler.ch>
|
||||
|
@ -157,7 +158,7 @@ EOF;
|
|||
|
||||
// Collect information from all packages.
|
||||
$packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages());
|
||||
$autoloads = $this->parseAutoloads($packageMap, $mainPackage);
|
||||
$autoloads = $this->parseAutoloads($packageMap, $mainPackage, $this->devMode === false);
|
||||
|
||||
// Process the 'psr-0' base directories.
|
||||
foreach ($autoloads['psr-0'] as $namespace => $paths) {
|
||||
|
@ -312,6 +313,8 @@ EOF;
|
|||
'optimize' => (bool) $scanPsr0Packages,
|
||||
));
|
||||
}
|
||||
|
||||
return count($classMap);
|
||||
}
|
||||
|
||||
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, array $classMap = array())
|
||||
|
@ -383,11 +386,15 @@ EOF;
|
|||
*
|
||||
* @param array $packageMap array of array(package, installDir-relative-to-composer.json)
|
||||
* @param PackageInterface $mainPackage root package instance
|
||||
* @param bool $filterOutRequireDevPackages whether to filter out require-dev packages
|
||||
* @return array array('psr-0' => array('Ns\\Foo' => array('installDir')))
|
||||
*/
|
||||
public function parseAutoloads(array $packageMap, PackageInterface $mainPackage)
|
||||
public function parseAutoloads(array $packageMap, PackageInterface $mainPackage, $filterOutRequireDevPackages = false)
|
||||
{
|
||||
$mainPackageMap = array_shift($packageMap);
|
||||
if ($filterOutRequireDevPackages) {
|
||||
$packageMap = $this->filterPackageMap($packageMap, $mainPackage);
|
||||
}
|
||||
$sortedPackageMap = $this->sortPackageMap($packageMap);
|
||||
$sortedPackageMap[] = $mainPackageMap;
|
||||
array_unshift($packageMap, $mainPackageMap);
|
||||
|
@ -539,7 +546,7 @@ EOF;
|
|||
}
|
||||
}
|
||||
|
||||
if (preg_match('/\.phar.+$/', $path)) {
|
||||
if (strpos($path, '.phar') !== false) {
|
||||
$baseDir = "'phar://' . " . $baseDir;
|
||||
}
|
||||
|
||||
|
@ -763,10 +770,14 @@ HEADER;
|
|||
$filesystem = new Filesystem();
|
||||
|
||||
$vendorPathCode = ' => ' . $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true, true) . " . '/";
|
||||
$vendorPharPathCode = ' => \'phar://\' . ' . $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true, true) . " . '/";
|
||||
$appBaseDirCode = ' => ' . $filesystem->findShortestPathCode(realpath($targetDir), $basePath, true, true) . " . '/";
|
||||
$appBaseDirPharCode = ' => \'phar://\' . ' . $filesystem->findShortestPathCode(realpath($targetDir), $basePath, true, true) . " . '/";
|
||||
|
||||
$absoluteVendorPathCode = ' => ' . substr(var_export(rtrim($vendorDir, '\\/') . '/', true), 0, -1);
|
||||
$absoluteVendorPharPathCode = ' => ' . substr(var_export(rtrim('phar://' . $vendorDir, '\\/') . '/', true), 0, -1);
|
||||
$absoluteAppBaseDirCode = ' => ' . substr(var_export(rtrim($baseDir, '\\/') . '/', true), 0, -1);
|
||||
$absoluteAppBaseDirPharCode = ' => ' . substr(var_export(rtrim('phar://' . $baseDir, '\\/') . '/', true), 0, -1);
|
||||
|
||||
$initializer = '';
|
||||
$prefix = "\0Composer\Autoload\ClassLoader\0";
|
||||
|
@ -789,9 +800,15 @@ HEADER;
|
|||
// See https://bugs.php.net/68057
|
||||
$staticPhpVersion = 70000;
|
||||
}
|
||||
$value = var_export($value, true);
|
||||
$value = str_replace($absoluteVendorPathCode, $vendorPathCode, $value);
|
||||
$value = str_replace($absoluteAppBaseDirCode, $appBaseDirCode, $value);
|
||||
$value = strtr(
|
||||
var_export($value, true),
|
||||
array(
|
||||
$absoluteVendorPathCode => $vendorPathCode,
|
||||
$absoluteVendorPharPathCode => $vendorPharPathCode,
|
||||
$absoluteAppBaseDirCode => $appBaseDirCode,
|
||||
$absoluteAppBaseDirPharCode => $appBaseDirPharCode,
|
||||
)
|
||||
);
|
||||
$value = ltrim(preg_replace('/^ */m', ' $0$0', $value));
|
||||
|
||||
$file .= sprintf(" public static $%s = %s;\n\n", $prop, $value);
|
||||
|
@ -899,6 +916,52 @@ INITIALIZER;
|
|||
return md5($package->getName() . ':' . $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out dev-dependencies
|
||||
*
|
||||
* @param array $packageMap
|
||||
* @param PackageInterface $mainPackage
|
||||
* @return array
|
||||
*/
|
||||
protected function filterPackageMap(array $packageMap, PackageInterface $mainPackage)
|
||||
{
|
||||
$packages = array();
|
||||
$include = array();
|
||||
|
||||
foreach ($packageMap as $item) {
|
||||
$package = $item[0];
|
||||
$name = $package->getName();
|
||||
$packages[$name] = $package;
|
||||
}
|
||||
|
||||
$add = function (PackageInterface $package) use (&$add, $packages, &$include) {
|
||||
foreach ($package->getRequires() as $link) {
|
||||
$target = $link->getTarget();
|
||||
if (!isset($include[$target])) {
|
||||
$include[$target] = true;
|
||||
if (isset($packages[$target])) {
|
||||
$add($packages[$target]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$add($mainPackage);
|
||||
|
||||
return array_filter(
|
||||
$packageMap,
|
||||
function ($item) use ($include) {
|
||||
$package = $item[0];
|
||||
foreach ($package->getNames() as $name) {
|
||||
if (isset($include[$name])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts packages by dependency weight
|
||||
*
|
||||
|
@ -911,80 +974,21 @@ INITIALIZER;
|
|||
{
|
||||
$packages = array();
|
||||
$paths = array();
|
||||
$usageList = array();
|
||||
|
||||
foreach ($packageMap as $item) {
|
||||
list($package, $path) = $item;
|
||||
$name = $package->getName();
|
||||
$packages[$name] = $package;
|
||||
$paths[$name] = $path;
|
||||
|
||||
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) {
|
||||
$target = $link->getTarget();
|
||||
$usageList[$target][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$computing = array();
|
||||
$computed = array();
|
||||
$computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
|
||||
// reusing computed importance
|
||||
if (isset($computed[$name])) {
|
||||
return $computed[$name];
|
||||
}
|
||||
$sortedPackages = PackageSorter::sortPackages($packages);
|
||||
|
||||
// canceling circular dependency
|
||||
if (isset($computing[$name])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$computing[$name] = true;
|
||||
$weight = 0;
|
||||
|
||||
if (isset($usageList[$name])) {
|
||||
foreach ($usageList[$name] as $user) {
|
||||
$weight -= 1 - $computeImportance($user);
|
||||
}
|
||||
}
|
||||
|
||||
unset($computing[$name]);
|
||||
$computed[$name] = $weight;
|
||||
|
||||
return $weight;
|
||||
};
|
||||
|
||||
$weightList = array();
|
||||
|
||||
foreach ($packages as $name => $package) {
|
||||
$weight = $computeImportance($name);
|
||||
$weightList[$name] = $weight;
|
||||
}
|
||||
|
||||
$stable_sort = function (&$array) {
|
||||
static $transform, $restore;
|
||||
|
||||
$i = 0;
|
||||
|
||||
if (!$transform) {
|
||||
$transform = function (&$v, $k) use (&$i) {
|
||||
$v = array($v, ++$i, $k, $v);
|
||||
};
|
||||
|
||||
$restore = function (&$v, $k) {
|
||||
$v = $v[3];
|
||||
};
|
||||
}
|
||||
|
||||
array_walk($array, $transform);
|
||||
asort($array);
|
||||
array_walk($array, $restore);
|
||||
};
|
||||
|
||||
$stable_sort($weightList);
|
||||
|
||||
$sortedPackageMap = array();
|
||||
|
||||
foreach (array_keys($weightList) as $name) {
|
||||
foreach ($sortedPackages as $package) {
|
||||
$name = $package->getName();
|
||||
$sortedPackageMap[] = array($packages[$name], $paths[$name]);
|
||||
}
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ class ClassLoader
|
|||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,9 +379,9 @@ class ClassLoader
|
|||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$length = $this->prefixLengthsPsr4[$first][$search];
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,10 @@ class ClassMapGenerator
|
|||
if ($blacklist && preg_match($blacklist, strtr(realpath($filePath), '\\', '/'))) {
|
||||
continue;
|
||||
}
|
||||
// check non-realpath of file for directories symlink in project dir
|
||||
if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classes = self::findClasses($filePath);
|
||||
|
||||
|
@ -158,7 +162,7 @@ class ClassMapGenerator
|
|||
}
|
||||
|
||||
// strip heredocs/nowdocs
|
||||
$contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
|
||||
$contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents);
|
||||
// strip strings
|
||||
$contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
|
||||
// strip leading non-php code if needed
|
||||
|
@ -175,6 +179,10 @@ class ClassMapGenerator
|
|||
if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
|
||||
$contents = substr($contents, 0, $pos);
|
||||
}
|
||||
// strip comments if short open tags are in the file
|
||||
if (preg_match('{(<\?)(?!(php|hh))}i', $contents)) {
|
||||
$contents = preg_replace('{//.* | /\*(?:[^*]++|\*(?!/))*\*/}x', '', $contents);
|
||||
}
|
||||
|
||||
preg_match_all('{
|
||||
(?:
|
||||
|
|
|
@ -44,7 +44,7 @@ class Cache
|
|||
$this->whitelist = $whitelist;
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
|
||||
if (preg_match('{(^|[\\\\/])(\$null|NUL|/dev/null)([\\\\/]|$)}', $cacheDir)) {
|
||||
if (!self::isUsable($cacheDir)) {
|
||||
$this->enabled = false;
|
||||
|
||||
return;
|
||||
|
@ -59,6 +59,11 @@ class Cache
|
|||
}
|
||||
}
|
||||
|
||||
public static function isUsable($path)
|
||||
{
|
||||
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
|
||||
}
|
||||
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->enabled;
|
||||
|
@ -71,12 +76,14 @@ class Cache
|
|||
|
||||
public function read($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
if (file_exists($this->root . $file)) {
|
||||
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
|
||||
|
||||
return file_get_contents($this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -142,8 +149,9 @@ class Cache
|
|||
*/
|
||||
public function copyTo($file, $target)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
if (file_exists($this->root . $file)) {
|
||||
try {
|
||||
touch($this->root . $file, filemtime($this->root . $file), time());
|
||||
} catch (\ErrorException $e) {
|
||||
|
@ -156,6 +164,7 @@ class Cache
|
|||
|
||||
return copy($this->root . $file, $target);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -167,10 +176,12 @@ class Cache
|
|||
|
||||
public function remove($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
if (file_exists($this->root . $file)) {
|
||||
return $this->filesystem->unlink($this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -178,7 +189,8 @@ class Cache
|
|||
public function clear()
|
||||
{
|
||||
if ($this->enabled) {
|
||||
return $this->filesystem->removeDirectory($this->root);
|
||||
$this->filesystem->emptyDirectory($this->root);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -216,20 +228,24 @@ class Cache
|
|||
|
||||
public function sha1($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
if (file_exists($this->root . $file)) {
|
||||
return sha1_file($this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function sha256($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
if (file_exists($this->root . $file)) {
|
||||
return hash_file('sha256', $this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ class AboutCommand extends BaseCommand
|
|||
$this
|
||||
->setName('about')
|
||||
->setDescription('Shows the short information about Composer.')
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
<info>php composer.phar about</info>
|
||||
EOT
|
||||
)
|
||||
|
@ -34,8 +35,9 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->getIO()->write(<<<EOT
|
||||
<info>Composer - Package Management for PHP</info>
|
||||
$this->getIO()->write(
|
||||
<<<EOT
|
||||
<info>Composer - Dependency Manager for PHP</info>
|
||||
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
|
||||
See https://getcomposer.org/ for more information.</comment>
|
||||
EOT
|
||||
|
|
|
@ -22,6 +22,7 @@ use Composer\Script\ScriptEvents;
|
|||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Loop;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -48,13 +49,15 @@ class ArchiveCommand extends BaseCommand
|
|||
.' Note that the format will be appended.'),
|
||||
new InputOption('ignore-filters', false, InputOption::VALUE_NONE, 'Ignore filters when saving package'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>archive</info> command creates an archive of the specified format
|
||||
containing the files and directories of the Composer project or the specified
|
||||
package in the specified version and writes it to the specified directory.
|
||||
|
||||
<info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#archive
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -66,8 +69,9 @@ EOT
|
|||
$composer = $this->getComposer(false);
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
|
||||
$eventDispatcher = $composer->getEventDispatcher();
|
||||
$eventDispatcher->dispatch($commandEvent->getName(), $commandEvent);
|
||||
$eventDispatcher->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
|
||||
}
|
||||
|
||||
if (null === $input->getOption('format')) {
|
||||
|
@ -102,8 +106,9 @@ EOT
|
|||
$archiveManager = $composer->getArchiveManager();
|
||||
} else {
|
||||
$factory = new Factory;
|
||||
$downloadManager = $factory->createDownloadManager($io, $config);
|
||||
$archiveManager = $factory->createArchiveManager($config, $downloadManager);
|
||||
$httpDownloader = $factory->createHttpDownloader($io, $config);
|
||||
$downloadManager = $factory->createDownloadManager($io, $config, $httpDownloader);
|
||||
$archiveManager = $factory->createArchiveManager($config, $downloadManager, new Loop($httpDownloader));
|
||||
}
|
||||
|
||||
if ($packageName) {
|
||||
|
|
|
@ -15,8 +15,11 @@ namespace Composer\Command;
|
|||
use Composer\Composer;
|
||||
use Composer\Config;
|
||||
use Composer\Console\Application;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Plugin\PreCommandRunEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
@ -24,13 +27,15 @@ use Symfony\Component\Console\Command\Command;
|
|||
/**
|
||||
* Base class for Composer commands
|
||||
*
|
||||
* @method Application getApplication()
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knplabs.com>
|
||||
* @author Konstantin Kudryashov <ever.zet@gmail.com>
|
||||
*/
|
||||
abstract class BaseCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var Composer
|
||||
* @var Composer|null
|
||||
*/
|
||||
private $composer;
|
||||
|
||||
|
@ -43,7 +48,7 @@ abstract class BaseCommand extends Command
|
|||
* @param bool $required
|
||||
* @param bool|null $disablePlugins
|
||||
* @throws \RuntimeException
|
||||
* @return Composer
|
||||
* @return Composer|null
|
||||
*/
|
||||
public function getComposer($required = true, $disablePlugins = null)
|
||||
{
|
||||
|
@ -123,6 +128,17 @@ abstract class BaseCommand extends Command
|
|||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// initialize a plugin-enabled Composer instance, either local or global
|
||||
$disablePlugins = $input->hasParameterOption('--no-plugins');
|
||||
$composer = $this->getComposer(false, $disablePlugins);
|
||||
if (null === $composer) {
|
||||
$composer = Factory::createGlobal($this->getIO(), $disablePlugins);
|
||||
}
|
||||
if ($composer) {
|
||||
$preCommandRunEvent = new PreCommandRunEvent(PluginEvents::PRE_COMMAND_RUN, $input, $this->getName());
|
||||
$composer->getEventDispatcher()->dispatch($preCommandRunEvent->getName(), $preCommandRunEvent);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(array('--no-ansi')) && $input->hasOption('no-progress')) {
|
||||
$input->setOption('no-progress', true);
|
||||
}
|
||||
|
@ -159,7 +175,7 @@ abstract class BaseCommand extends Command
|
|||
|
||||
if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'))) {
|
||||
$preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'));
|
||||
$preferDist = $input->getOption('prefer-dist');
|
||||
$preferDist = (bool) $input->getOption('prefer-dist');
|
||||
}
|
||||
|
||||
return array($preferSource, $preferDist);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\ArrayRepository;
|
||||
|
@ -21,6 +20,7 @@ use Composer\Repository\PlatformRepository;
|
|||
use Composer\Repository\RepositoryFactory;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
|
@ -71,15 +71,15 @@ class BaseDependencyCommand extends BaseCommand
|
|||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
// Prepare repositories and set up a pool
|
||||
// Prepare repositories and set up a repo set
|
||||
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
|
||||
$repository = new CompositeRepository(array(
|
||||
new ArrayRepository(array($composer->getPackage())),
|
||||
$composer->getRepositoryManager()->getLocalRepository(),
|
||||
new PlatformRepository(array(), $platformOverrides),
|
||||
));
|
||||
$pool = new Pool();
|
||||
$pool->addRepository($repository);
|
||||
$repositorySet = new RepositorySet();
|
||||
$repositorySet->addRepository($repository);
|
||||
|
||||
// Parse package name and constraint
|
||||
list($needle, $textConstraint) = array_pad(
|
||||
|
@ -89,7 +89,7 @@ class BaseDependencyCommand extends BaseCommand
|
|||
);
|
||||
|
||||
// Find packages that are or provide the requested package first
|
||||
$packages = $pool->whatProvides($needle);
|
||||
$packages = $repositorySet->findPackages(strtolower($needle), null, false);
|
||||
if (empty($packages)) {
|
||||
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
|
||||
}
|
||||
|
@ -129,8 +129,11 @@ class BaseDependencyCommand extends BaseCommand
|
|||
$results = $repository->getDependents($needles, $constraint, $inverted, $recursive);
|
||||
if (empty($results)) {
|
||||
$extra = (null !== $constraint) ? sprintf(' in versions %smatching %s', $inverted ? 'not ' : '', $textConstraint) : '';
|
||||
$this->getIO()->writeError(sprintf('<info>There is no installed package depending on "%s"%s</info>',
|
||||
$needle, $extra));
|
||||
$this->getIO()->writeError(sprintf(
|
||||
'<info>There is no installed package depending on "%s"%s</info>',
|
||||
$needle,
|
||||
$extra
|
||||
));
|
||||
} elseif ($renderTree) {
|
||||
$this->initStyles($output);
|
||||
$root = $packages[0];
|
||||
|
@ -180,8 +183,9 @@ class BaseDependencyCommand extends BaseCommand
|
|||
// Render table
|
||||
$renderer = new Table($output);
|
||||
$renderer->setStyle('compact');
|
||||
$renderer->getStyle()->setVerticalBorderChar('');
|
||||
$renderer->getStyle()->setCellRowContentFormat('%s ');
|
||||
$rendererStyle = $renderer->getStyle();
|
||||
$rendererStyle->setVerticalBorderChar('');
|
||||
$rendererStyle->setCellRowContentFormat('%s ');
|
||||
$renderer->setRows($table)->render();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ use Composer\Package\PackageInterface;
|
|||
use Composer\Semver\Constraint\Constraint;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
|
||||
|
@ -26,9 +27,15 @@ class CheckPlatformReqsCommand extends BaseCommand
|
|||
{
|
||||
$this->setName('check-platform-reqs')
|
||||
->setDescription('Check that platform requirements are satisfied.')
|
||||
->setHelp(<<<EOT
|
||||
->setDefinition(array(
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables checking of require-dev packages requirements.'),
|
||||
))
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
Checks that your PHP and extensions versions match the platform requirements of the installed packages.
|
||||
|
||||
Unlike update/install, this command will ignore config.platform settings and check the real platform packages so you can be certain you have the required platform dependencies.
|
||||
|
||||
<info>php composer.phar check-platform-reqs</info>
|
||||
|
||||
EOT
|
||||
|
@ -39,22 +46,27 @@ EOT
|
|||
{
|
||||
$composer = $this->getComposer();
|
||||
|
||||
$repos = $composer->getRepositoryManager()->getLocalRepository();
|
||||
|
||||
$allPackages = array_merge(array($composer->getPackage()), $repos->getPackages());
|
||||
$requires = $composer->getPackage()->getDevRequires();
|
||||
$requires = $composer->getPackage()->getRequires();
|
||||
if ($input->getOption('no-dev')) {
|
||||
$dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages();
|
||||
} else {
|
||||
$dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
|
||||
// fallback to lockfile if installed repo is empty
|
||||
if (!$dependencies) {
|
||||
$dependencies = $composer->getLocker()->getLockedRepository(true)->getPackages();
|
||||
}
|
||||
$requires += $composer->getPackage()->getDevRequires();
|
||||
}
|
||||
foreach ($requires as $require => $link) {
|
||||
$requires[$require] = array($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var PackageInterface $package
|
||||
*/
|
||||
foreach ($allPackages as $package) {
|
||||
foreach ($dependencies as $package) {
|
||||
foreach ($package->getRequires() as $require => $link) {
|
||||
$requires[$require][] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($requires);
|
||||
|
||||
$platformRepo = new PlatformRepository(array(), array());
|
||||
|
@ -73,7 +85,7 @@ EOT
|
|||
$exitCode = 0;
|
||||
|
||||
/**
|
||||
* @var Link $require
|
||||
* @var Link[] $links
|
||||
*/
|
||||
foreach ($requires as $require => $links) {
|
||||
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $require)) {
|
||||
|
@ -142,8 +154,9 @@ EOT
|
|||
// Render table
|
||||
$renderer = new Table($output);
|
||||
$renderer->setStyle('compact');
|
||||
$renderer->getStyle()->setVerticalBorderChar('');
|
||||
$renderer->getStyle()->setCellRowContentFormat('%s ');
|
||||
$rendererStyle = $renderer->getStyle();
|
||||
$rendererStyle->setVerticalBorderChar('');
|
||||
$rendererStyle->setCellRowContentFormat('%s ');
|
||||
$renderer->setRows($table)->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,12 @@ class ClearCacheCommand extends BaseCommand
|
|||
->setName('clear-cache')
|
||||
->setAliases(array('clearcache'))
|
||||
->setDescription('Clears composer\'s internal package cache.')
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>clear-cache</info> deletes all cached packages from composer's
|
||||
cache directory.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
|
|
@ -21,6 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
use Composer\Config;
|
||||
use Composer\Config\JsonConfigSource;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Semver\VersionParser;
|
||||
use Composer\Package\BasePackage;
|
||||
|
@ -75,7 +76,8 @@ class ConfigCommand extends BaseCommand
|
|||
new InputArgument('setting-key', null, 'Setting key'),
|
||||
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
This command allows you to edit composer config settings and repositories
|
||||
in either the local composer.json file or the global config.json file.
|
||||
|
||||
|
@ -123,6 +125,8 @@ You can always pass more than one option. As an example, if you want to edit the
|
|||
global config.json file.
|
||||
|
||||
<comment>%command.full_name% --editor --global</comment>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#config
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -224,7 +228,7 @@ EOT
|
|||
}
|
||||
|
||||
$settingKey = $input->getArgument('setting-key');
|
||||
if (!$settingKey) {
|
||||
if (!$settingKey || !is_string($settingKey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -283,7 +287,7 @@ EOT
|
|||
$value = json_encode($value);
|
||||
}
|
||||
|
||||
$this->getIO()->write($value);
|
||||
$this->getIO()->write($value, true, IOInterface::QUIET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -301,6 +305,7 @@ EOT
|
|||
$uniqueConfigValues = array(
|
||||
'process-timeout' => array('is_numeric', 'intval'),
|
||||
'use-include-path' => array($booleanValidator, $booleanNormalizer),
|
||||
'use-github-api' => array($booleanValidator, $booleanNormalizer),
|
||||
'preferred-install' => array(
|
||||
function ($val) {
|
||||
return in_array($val, array('auto', 'source', 'dist'), true);
|
||||
|
@ -454,6 +459,10 @@ EOT
|
|||
);
|
||||
|
||||
if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) {
|
||||
if ($settingKey === 'disable-tls' && $this->config->get('disable-tls')) {
|
||||
$this->getIO()->writeError('<info>You are now running Composer with SSL/TLS protection enabled.</info>');
|
||||
}
|
||||
|
||||
return $this->configSource->removeConfigSetting($settingKey);
|
||||
}
|
||||
if (isset($uniqueConfigValues[$settingKey])) {
|
||||
|
@ -612,6 +621,15 @@ EOT
|
|||
return;
|
||||
}
|
||||
|
||||
// handle script
|
||||
if (preg_match('/^scripts\.(.+)/', $settingKey, $matches)) {
|
||||
if ($input->getOption('unset')) {
|
||||
return $this->configSource->removeProperty($settingKey);
|
||||
}
|
||||
|
||||
return $this->configSource->addProperty($settingKey, count($values) > 1 ? $values : $values[0]);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
|
||||
}
|
||||
|
||||
|
@ -629,7 +647,17 @@ EOT
|
|||
));
|
||||
}
|
||||
|
||||
return call_user_func(array($this->configSource, $method), $key, $normalizer($values[0]));
|
||||
$normalizedValue = $normalizer($values[0]);
|
||||
|
||||
if ($key === 'disable-tls') {
|
||||
if (!$normalizedValue && $this->config->get('disable-tls')) {
|
||||
$this->getIO()->writeError('<info>You are now running Composer with SSL/TLS protection enabled.</info>');
|
||||
} elseif ($normalizedValue && !$this->config->get('disable-tls')) {
|
||||
$this->getIO()->writeError('<warning>You are now running Composer with SSL/TLS protection disabled.</warning>');
|
||||
}
|
||||
}
|
||||
|
||||
return call_user_func(array($this->configSource, $method), $key, $normalizedValue);
|
||||
}
|
||||
|
||||
protected function handleMultiValue($key, array $callbacks, array $values, $method)
|
||||
|
@ -685,9 +713,9 @@ EOT
|
|||
}
|
||||
|
||||
if (is_string($rawVal) && $rawVal != $value) {
|
||||
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
|
||||
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>', true, IOInterface::QUIET);
|
||||
} else {
|
||||
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
|
||||
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>', true, IOInterface::QUIET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ use Composer\Installer\InstallationManager;
|
|||
use Composer\Installer\SuggestedPackagesReporter;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\Package\Version\VersionSelector;
|
||||
use Composer\Package\AliasPackage;
|
||||
|
@ -28,6 +27,7 @@ use Composer\Repository\RepositoryFactory;
|
|||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\InstalledFilesystemRepository;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Composer\Script\ScriptEvents;
|
||||
use Composer\Util\Silencer;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
@ -38,6 +38,7 @@ use Symfony\Component\Finder\Finder;
|
|||
use Composer\Json\JsonFile;
|
||||
use Composer\Config\JsonConfigSource;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Loop;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
|
||||
/**
|
||||
|
@ -75,11 +76,13 @@ class CreateProjectCommand extends BaseCommand
|
|||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Whether to prevent execution of all defined scripts in the root package.'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
new InputOption('no-secure-http', null, InputOption::VALUE_NONE, 'Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.'),
|
||||
new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deletion vcs folder.'),
|
||||
new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'),
|
||||
new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'),
|
||||
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'),
|
||||
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>create-project</info> command creates a new project from a given
|
||||
package into a new directory. If executed without params and in a directory
|
||||
with a composer.json file it installs the packages for the current project.
|
||||
|
@ -102,6 +105,7 @@ controlled code by appending the <info>'--prefer-source'</info> flag.
|
|||
To install a package from another repository than the default one you
|
||||
can pass the <info>'--repository=https://myrepository.org'</info> flag.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#create-project
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -136,7 +140,6 @@ EOT
|
|||
$input->getOption('repository') ?: $input->getOption('repository-url'),
|
||||
$input->getOption('no-plugins'),
|
||||
$input->getOption('no-scripts'),
|
||||
$input->getOption('keep-vcs'),
|
||||
$input->getOption('no-progress'),
|
||||
$input->getOption('no-install'),
|
||||
$input->getOption('ignore-platform-reqs'),
|
||||
|
@ -144,7 +147,7 @@ EOT
|
|||
);
|
||||
}
|
||||
|
||||
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true)
|
||||
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true)
|
||||
{
|
||||
$oldCwd = getcwd();
|
||||
|
||||
|
@ -154,13 +157,12 @@ EOT
|
|||
$this->suggestedPackagesReporter = new SuggestedPackagesReporter($io);
|
||||
|
||||
if ($packageName !== null) {
|
||||
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $keepVcs, $noProgress, $ignorePlatformReqs, $secureHttp);
|
||||
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp);
|
||||
} else {
|
||||
$installedFromVcs = false;
|
||||
}
|
||||
|
||||
$composer = Factory::create($io, null, $disablePlugins);
|
||||
$composer->getDownloadManager()->setOutputProgress(!$noProgress);
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
|
@ -182,7 +184,9 @@ EOT
|
|||
->setRunScripts(!$noScripts)
|
||||
->setIgnorePlatformRequirements($ignorePlatformReqs)
|
||||
->setSuggestedPackagesReporter($this->suggestedPackagesReporter)
|
||||
->setOptimizeAutoloader($config->get('optimize-autoloader'));
|
||||
->setOptimizeAutoloader($config->get('optimize-autoloader'))
|
||||
->setClassMapAuthoritative($config->get('classmap-authoritative'))
|
||||
->setApcuAutoloader($config->get('apcu-autoloader'));
|
||||
|
||||
if ($disablePlugins) {
|
||||
$installer->disablePlugins();
|
||||
|
@ -195,9 +199,12 @@ EOT
|
|||
}
|
||||
|
||||
$hasVcs = $installedFromVcs;
|
||||
if (!$keepVcs && $installedFromVcs
|
||||
if (
|
||||
!$input->getOption('keep-vcs')
|
||||
&& $installedFromVcs
|
||||
&& (
|
||||
!$io->isInteractive()
|
||||
$input->getOption('remove-vcs')
|
||||
|| !$io->isInteractive()
|
||||
|| $io->askConfirmation('<info>Do you want to remove the existing VCS (.git, .svn..) history?</info> [<comment>Y,n</comment>]? ', true)
|
||||
)
|
||||
) {
|
||||
|
@ -253,7 +260,7 @@ EOT
|
|||
return 0;
|
||||
}
|
||||
|
||||
protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true)
|
||||
protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true)
|
||||
{
|
||||
if (!$secureHttp) {
|
||||
$config->merge(array('config' => array('secure-http' => false)));
|
||||
|
@ -286,8 +293,8 @@ EOT
|
|||
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
|
||||
}
|
||||
|
||||
$pool = new Pool($stability);
|
||||
$pool->addRepository($sourceRepo);
|
||||
$repositorySet = new RepositorySet(array(), $stability);
|
||||
$repositorySet->addRepository($sourceRepo);
|
||||
|
||||
$phpVersion = null;
|
||||
$prettyPhpVersion = null;
|
||||
|
@ -301,7 +308,7 @@ EOT
|
|||
}
|
||||
|
||||
// find the latest version if there are multiple
|
||||
$versionSelector = new VersionSelector($pool);
|
||||
$versionSelector = new VersionSelector($repositorySet);
|
||||
$package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability);
|
||||
|
||||
if (!$package) {
|
||||
|
@ -319,14 +326,17 @@ EOT
|
|||
}
|
||||
|
||||
// handler Ctrl+C for unix-like systems
|
||||
if (function_exists('pcntl_signal')) {
|
||||
declare(ticks=100);
|
||||
pcntl_signal(SIGINT, function () use ($directory) {
|
||||
if (function_exists('pcntl_async_signals')) {
|
||||
@mkdir($directory, 0777, true);
|
||||
if ($realDir = realpath($directory)) {
|
||||
pcntl_async_signals(true);
|
||||
pcntl_signal(SIGINT, function () use ($realDir) {
|
||||
$fs = new Filesystem();
|
||||
$fs->removeDirectory($directory);
|
||||
$fs->removeDirectory($realDir);
|
||||
exit(130);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$io->writeError('<info>Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')</info>');
|
||||
|
||||
|
@ -338,19 +348,17 @@ EOT
|
|||
$package = $package->getAliasOf();
|
||||
}
|
||||
|
||||
if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
|
||||
$package->setSourceReference(substr($package->getPrettyVersion(), 4));
|
||||
}
|
||||
$factory = new Factory();
|
||||
|
||||
$dm = $this->createDownloadManager($io, $config);
|
||||
$httpDownloader = $factory->createHttpDownloader($io, $config);
|
||||
$dm = $factory->createDownloadManager($io, $config, $httpDownloader);
|
||||
$dm->setPreferSource($preferSource)
|
||||
->setPreferDist($preferDist)
|
||||
->setOutputProgress(!$noProgress);
|
||||
->setPreferDist($preferDist);
|
||||
|
||||
$projectInstaller = new ProjectInstaller($directory, $dm);
|
||||
$im = $this->createInstallationManager();
|
||||
$im = $factory->createInstallationManager(new Loop($httpDownloader));
|
||||
$im->addInstaller($projectInstaller);
|
||||
$im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
|
||||
$im->execute(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
|
||||
$im->notifyInstalls($io);
|
||||
|
||||
// collect suggestions
|
||||
|
@ -366,16 +374,4 @@ EOT
|
|||
|
||||
return $installedFromVcs;
|
||||
}
|
||||
|
||||
protected function createDownloadManager(IOInterface $io, Config $config)
|
||||
{
|
||||
$factory = new Factory();
|
||||
|
||||
return $factory->createDownloadManager($io, $config);
|
||||
}
|
||||
|
||||
protected function createInstallationManager()
|
||||
{
|
||||
return new InstallationManager();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,11 +31,13 @@ class DependsCommand extends BaseDependencyCommand
|
|||
->setName('depends')
|
||||
->setAliases(array('why'))
|
||||
->setDescription('Shows which packages cause the given package to be installed.')
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
Displays detailed information about where a package is referenced.
|
||||
|
||||
<info>php composer.phar depends composer/composer</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#depends-why-
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
|
|
@ -16,12 +16,13 @@ use Composer\Composer;
|
|||
use Composer\Factory;
|
||||
use Composer\Config;
|
||||
use Composer\Downloader\TransportException;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Util\ConfigValidator;
|
||||
use Composer\Util\IniHelper;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\StreamContextFactory;
|
||||
use Composer\SelfUpdate\Keys;
|
||||
use Composer\SelfUpdate\Versions;
|
||||
|
@ -34,8 +35,8 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
*/
|
||||
class DiagnoseCommand extends BaseCommand
|
||||
{
|
||||
/** @var RemoteFileSystem */
|
||||
protected $rfs;
|
||||
/** @var HttpDownloader */
|
||||
protected $httpDownloader;
|
||||
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
|
@ -48,11 +49,13 @@ class DiagnoseCommand extends BaseCommand
|
|||
$this
|
||||
->setName('diagnose')
|
||||
->setDescription('Diagnoses the system to identify common errors.')
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>diagnose</info> command checks common errors to help debugging problems.
|
||||
|
||||
The process exit code will be 1 in case of warnings and 2 for errors.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#diagnose
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -81,9 +84,9 @@ EOT
|
|||
}
|
||||
|
||||
$config->merge(array('config' => array('secure-http' => false)));
|
||||
$config->prohibitUrlByConfig('http://packagist.org', new NullIO);
|
||||
$config->prohibitUrlByConfig('http://repo.packagist.org', new NullIO);
|
||||
|
||||
$this->rfs = Factory::createRemoteFilesystem($io, $config);
|
||||
$this->httpDownloader = Factory::createHttpDownloader($io, $config);
|
||||
$this->process = new ProcessExecutor($io);
|
||||
|
||||
$io->write('Checking platform settings: ', false);
|
||||
|
@ -117,8 +120,9 @@ EOT
|
|||
$io->write('Checking github.com rate limit: ', false);
|
||||
try {
|
||||
$rate = $this->getGithubRateLimit('github.com');
|
||||
$this->outputResult(true);
|
||||
if (10 > $rate['remaining']) {
|
||||
if (!is_array($rate)) {
|
||||
$this->outputResult($rate);
|
||||
} elseif (10 > $rate['remaining']) {
|
||||
$io->write('<warning>WARNING</warning>');
|
||||
$io->write(sprintf(
|
||||
'<comment>Github has a rate limit on their API. '
|
||||
|
@ -129,6 +133,8 @@ EOT
|
|||
$rate['remaining'],
|
||||
$rate['limit']
|
||||
));
|
||||
} else {
|
||||
$this->outputResult(true);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof TransportException && $e->getCode() === 401) {
|
||||
|
@ -150,9 +156,17 @@ EOT
|
|||
$this->outputResult($this->checkVersion($config));
|
||||
}
|
||||
|
||||
$io->write(sprintf('Composer version: <comment>%s</comment>', Composer::VERSION));
|
||||
$io->write(sprintf('Composer version: <comment>%s</comment>', Composer::getVersion()));
|
||||
|
||||
$io->write(sprintf('PHP version: <comment>%s</comment>', PHP_VERSION));
|
||||
$platformOverrides = $config->get('platform') ?: array();
|
||||
$platformRepo = new PlatformRepository(array(), $platformOverrides);
|
||||
$phpPkg = $platformRepo->findPackage('php', '*');
|
||||
$phpVersion = $phpPkg->getPrettyVersion();
|
||||
if (false !== strpos($phpPkg->getDescription(), 'overridden')) {
|
||||
$phpVersion .= ' - ' . $phpPkg->getDescription();
|
||||
}
|
||||
|
||||
$io->write(sprintf('PHP version: <comment>%s</comment>', $phpVersion));
|
||||
|
||||
if (defined('PHP_BINARY')) {
|
||||
$io->write(sprintf('PHP binary path: <comment>%s</comment>', PHP_BINARY));
|
||||
|
@ -197,6 +211,11 @@ EOT
|
|||
|
||||
private function checkHttp($proto, Config $config)
|
||||
{
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$disableTls = false;
|
||||
$result = array();
|
||||
if ($proto === 'https' && $config->get('disable-tls') === true) {
|
||||
|
@ -208,7 +227,7 @@ EOT
|
|||
}
|
||||
|
||||
try {
|
||||
$this->rfs->getContents('packagist.org', $proto . '://packagist.org/packages.json', false);
|
||||
$this->httpDownloader->get($proto . '://repo.packagist.org/packages.json');
|
||||
} catch (TransportException $e) {
|
||||
if (false !== strpos($e->getMessage(), 'cafile')) {
|
||||
$result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
|
||||
|
@ -228,13 +247,18 @@ EOT
|
|||
|
||||
private function checkHttpProxy()
|
||||
{
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$protocol = extension_loaded('openssl') ? 'https' : 'http';
|
||||
try {
|
||||
$json = json_decode($this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false), true);
|
||||
$json = $this->httpDownloader->get($protocol . '://repo.packagist.org/packages.json')->decodeJson();
|
||||
$hash = reset($json['provider-includes']);
|
||||
$hash = $hash['sha256'];
|
||||
$path = str_replace('%hash%', $hash, key($json['provider-includes']));
|
||||
$provider = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/'.$path, false);
|
||||
$provider = $this->httpDownloader->get($protocol . '://repo.packagist.org/'.$path)->getBody();
|
||||
|
||||
if (hash('sha256', $provider) !== $hash) {
|
||||
return 'It seems that your proxy is modifying http traffic on the fly';
|
||||
|
@ -255,12 +279,17 @@ EOT
|
|||
*/
|
||||
private function checkHttpProxyFullUriRequestParam()
|
||||
{
|
||||
$url = 'http://packagist.org/packages.json';
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$url = 'http://repo.packagist.org/packages.json';
|
||||
try {
|
||||
$this->rfs->getContents('packagist.org', $url, false);
|
||||
$this->httpDownloader->get($url);
|
||||
} catch (TransportException $e) {
|
||||
try {
|
||||
$this->rfs->getContents('packagist.org', $url, false, array('http' => array('request_fulluri' => false)));
|
||||
$this->httpDownloader->get($url, array('http' => array('request_fulluri' => false)));
|
||||
} catch (TransportException $e) {
|
||||
return 'Unable to assess the situation, maybe packagist.org is down ('.$e->getMessage().')';
|
||||
}
|
||||
|
@ -280,16 +309,21 @@ EOT
|
|||
*/
|
||||
private function checkHttpsProxyFullUriRequestParam()
|
||||
{
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!extension_loaded('openssl')) {
|
||||
return 'You need the openssl extension installed for this check';
|
||||
}
|
||||
|
||||
$url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0';
|
||||
try {
|
||||
$this->rfs->getContents('github.com', $url, false);
|
||||
$this->httpDownloader->get($url);
|
||||
} catch (TransportException $e) {
|
||||
try {
|
||||
$this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false)));
|
||||
$this->httpDownloader->get($url, array('http' => array('request_fulluri' => false)));
|
||||
} catch (TransportException $e) {
|
||||
return 'Unable to assess the situation, maybe github is down ('.$e->getMessage().')';
|
||||
}
|
||||
|
@ -302,11 +336,16 @@ EOT
|
|||
|
||||
private function checkGithubOauth($domain, $token)
|
||||
{
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
|
||||
try {
|
||||
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/' : 'https://'.$domain.'/api/v3/';
|
||||
|
||||
return $this->rfs->getContents($domain, $url, false, array(
|
||||
return $this->httpDownloader->get($url, array(
|
||||
'retry-auth-failure' => false,
|
||||
)) ? true : 'Unexpected error';
|
||||
} catch (\Exception $e) {
|
||||
|
@ -322,17 +361,21 @@ EOT
|
|||
* @param string $domain
|
||||
* @param string $token
|
||||
* @throws TransportException
|
||||
* @return array
|
||||
* @return array|string
|
||||
*/
|
||||
private function getGithubRateLimit($domain, $token = null)
|
||||
{
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($token) {
|
||||
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
|
||||
}
|
||||
|
||||
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
|
||||
$json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false));
|
||||
$data = json_decode($json, true);
|
||||
$data = $this->httpDownloader->get($url, array('retry-auth-failure' => false))->decodeJson();
|
||||
|
||||
return $data['resources']['core'];
|
||||
}
|
||||
|
@ -380,7 +423,12 @@ EOT
|
|||
|
||||
private function checkVersion($config)
|
||||
{
|
||||
$versionsUtil = new Versions($config, $this->rfs);
|
||||
$result = $this->checkConnectivity();
|
||||
if ($result !== true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$versionsUtil = new Versions($config, $this->httpDownloader);
|
||||
$latest = $versionsUtil->getLatest();
|
||||
|
||||
if (Composer::VERSION !== $latest['version'] && Composer::VERSION !== '@package_version@') {
|
||||
|
@ -403,6 +451,7 @@ EOT
|
|||
}
|
||||
|
||||
$hadError = false;
|
||||
$hadWarning = false;
|
||||
if ($result instanceof \Exception) {
|
||||
$result = '<error>['.get_class($result).'] '.$result->getMessage().'</error>';
|
||||
}
|
||||
|
@ -417,16 +466,18 @@ EOT
|
|||
foreach ($result as $message) {
|
||||
if (false !== strpos($message, '<error>')) {
|
||||
$hadError = true;
|
||||
} elseif (false !== strpos($message, '<warning>')) {
|
||||
$hadWarning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hadError) {
|
||||
$io->write('<error>FAIL</error>');
|
||||
$this->exitCode = 2;
|
||||
} else {
|
||||
$this->exitCode = max($this->exitCode, 2);
|
||||
} elseif ($hadWarning) {
|
||||
$io->write('<warning>WARNING</warning>');
|
||||
$this->exitCode = 1;
|
||||
$this->exitCode = max($this->exitCode, 1);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
|
@ -471,7 +522,7 @@ EOT
|
|||
$errors['iconv_mbstring'] = true;
|
||||
}
|
||||
|
||||
if (!ini_get('allow_url_fopen')) {
|
||||
if (!filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
$errors['allow_url_fopen'] = true;
|
||||
}
|
||||
|
||||
|
@ -495,7 +546,7 @@ EOT
|
|||
$warnings['openssl_version'] = true;
|
||||
}
|
||||
|
||||
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
|
||||
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
$warnings['apc_cli'] = true;
|
||||
}
|
||||
|
||||
|
@ -518,7 +569,7 @@ EOT
|
|||
}
|
||||
}
|
||||
|
||||
if (ini_get('xdebug.profiler_enabled')) {
|
||||
if (filter_var(ini_get('xdebug.profiler_enabled'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
$warnings['xdebug_profile'] = true;
|
||||
} elseif (extension_loaded('xdebug')) {
|
||||
$warnings['xdebug_loaded'] = true;
|
||||
|
@ -552,20 +603,6 @@ EOT
|
|||
$text .= "Install either of them or recompile php without --disable-iconv";
|
||||
break;
|
||||
|
||||
case 'unicode':
|
||||
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
|
||||
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
|
||||
$text .= " detect_unicode = Off";
|
||||
$displayIniMessage = true;
|
||||
break;
|
||||
|
||||
case 'suhosin':
|
||||
$text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL;
|
||||
$text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL;
|
||||
$text .= " suhosin.executor.include.whitelist = phar ".$current;
|
||||
$displayIniMessage = true;
|
||||
break;
|
||||
|
||||
case 'php':
|
||||
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
|
||||
break;
|
||||
|
@ -658,4 +695,20 @@ EOT
|
|||
|
||||
return !$warnings && !$errors ? true : $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if allow_url_fopen is ON
|
||||
*
|
||||
* @return true|string
|
||||
*/
|
||||
private function checkConnectivity()
|
||||
{
|
||||
if (!ini_get('allow_url_fopen')) {
|
||||
$result = '<info>Skipped because allow_url_fopen is missing.</info>';
|
||||
return $result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,8 +36,11 @@ class DumpAutoloadCommand extends BaseCommand
|
|||
new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
<info>php composer.phar dump-autoload</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#dump-autoload-dumpautoload-
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -60,11 +63,11 @@ EOT
|
|||
$apcu = $input->getOption('apcu') || $config->get('apcu-autoloader');
|
||||
|
||||
if ($authoritative) {
|
||||
$this->getIO()->writeError('<info>Generating optimized autoload files (authoritative)</info>');
|
||||
$this->getIO()->writeError('<info>Generating optimized autoload files (authoritative)</info>', false);
|
||||
} elseif ($optimize) {
|
||||
$this->getIO()->writeError('<info>Generating optimized autoload files</info>');
|
||||
$this->getIO()->writeError('<info>Generating optimized autoload files</info>', false);
|
||||
} else {
|
||||
$this->getIO()->writeError('<info>Generating autoload files</info>');
|
||||
$this->getIO()->writeError('<info>Generating autoload files</info>', false);
|
||||
}
|
||||
|
||||
$generator = $composer->getAutoloadGenerator();
|
||||
|
@ -72,6 +75,14 @@ EOT
|
|||
$generator->setClassMapAuthoritative($authoritative);
|
||||
$generator->setApcu($apcu);
|
||||
$generator->setRunScripts(!$input->getOption('no-scripts'));
|
||||
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
||||
$numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
||||
|
||||
if ($authoritative) {
|
||||
$this->getIO()->overwriteError('<info>Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes</info>');
|
||||
} elseif ($optimize) {
|
||||
$this->getIO()->overwriteError('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>');
|
||||
} else {
|
||||
$this->getIO()->overwriteError('<info>Generated autoload files containing '. $numberOfClasses .' classes</info>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,13 @@ class ExecCommand extends BaseCommand
|
|||
'Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments'
|
||||
),
|
||||
))
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
Executes a vendored binary/script.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#exec
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
|
@ -53,7 +60,8 @@ class ExecCommand extends BaseCommand
|
|||
throw new \RuntimeException("No binaries found in composer.json or in bin-dir ($binDir)");
|
||||
}
|
||||
|
||||
$this->getIO()->write(<<<EOT
|
||||
$this->getIO()->write(
|
||||
<<<EOT
|
||||
<comment>Available binaries:</comment>
|
||||
EOT
|
||||
);
|
||||
|
@ -66,7 +74,8 @@ EOT
|
|||
|
||||
$previousBin = $bin;
|
||||
$bin = basename($bin);
|
||||
$this->getIO()->write(<<<EOT
|
||||
$this->getIO()->write(
|
||||
<<<EOT
|
||||
<info>- $bin</info>
|
||||
EOT
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Factory;
|
||||
use Composer\Util\Filesystem;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\StringInput;
|
||||
|
@ -32,7 +33,8 @@ class GlobalCommand extends BaseCommand
|
|||
new InputArgument('command-name', InputArgument::REQUIRED, ''),
|
||||
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
Use this command as a wrapper to run other Composer commands
|
||||
within the global context of COMPOSER_HOME.
|
||||
|
||||
|
@ -48,6 +50,7 @@ XDG_CONFIG_HOME or default to /home/<user>/.config/composer
|
|||
Note: This path may vary depending on customizations to bin-dir in
|
||||
composer.json or the environmental variable COMPOSER_BIN_DIR.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#global
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -74,8 +77,22 @@ EOT
|
|||
|
||||
// change to global dir
|
||||
$config = Factory::createConfig();
|
||||
chdir($config->get('home'));
|
||||
$this->getIO()->writeError('<info>Changed current directory to '.$config->get('home').'</info>');
|
||||
$home = $config->get('home');
|
||||
|
||||
if (!is_dir($home)) {
|
||||
$fs = new Filesystem();
|
||||
$fs->ensureDirectoryExists($home);
|
||||
if (!is_dir($home)) {
|
||||
throw new \RuntimeException('Could not create home directory');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
chdir($home);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException('Could not switch to home directory "'.$home.'"', 0, $e);
|
||||
}
|
||||
$this->getIO()->writeError('<info>Changed current directory to '.$home.'</info>');
|
||||
|
||||
// create new input without "global" command prefix
|
||||
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
|
||||
|
|
|
@ -42,12 +42,15 @@ class HomeCommand extends BaseCommand
|
|||
new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'),
|
||||
new InputOption('show', 's', InputOption::VALUE_NONE, 'Only show the homepage or repository URL.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The home command opens or shows a package's repository URL or
|
||||
homepage in your default browser.
|
||||
|
||||
To open the homepage by default, use -H or --homepage.
|
||||
To show instead of open the repository or homepage URL, use -s or --show.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#browse-home
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Factory;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\BasePackage;
|
||||
|
@ -21,7 +20,9 @@ use Composer\Package\Version\VersionSelector;
|
|||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositoryFactory;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -40,8 +41,8 @@ class InitCommand extends BaseCommand
|
|||
/** @var array */
|
||||
private $gitConfig;
|
||||
|
||||
/** @var Pool[] */
|
||||
private $pools;
|
||||
/** @var RepositorySet[] */
|
||||
private $repositorySets;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -64,12 +65,14 @@ class InitCommand extends BaseCommand
|
|||
new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
|
||||
new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>init</info> command creates a basic composer.json file
|
||||
in the current directory.
|
||||
|
||||
<info>php composer.phar init</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#init
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -144,6 +147,11 @@ EOT
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$question = 'Would you like to install dependencies now [<comment>yes</comment>]? ';
|
||||
if ($input->isInteractive() && $this->hasDependencies($options) && $io->askConfirmation($question, true)) {
|
||||
$this->installDependencies($output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,13 +168,25 @@ EOT
|
|||
if ($repositories) {
|
||||
$config = Factory::createConfig($io);
|
||||
$repos = array(new PlatformRepository);
|
||||
$createDefaultPackagistRepo = true;
|
||||
foreach ($repositories as $repo) {
|
||||
$repos[] = RepositoryFactory::fromString($io, $config, $repo);
|
||||
$repoConfig = RepositoryFactory::configFromString($io, $config, $repo);
|
||||
if (
|
||||
(isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false))
|
||||
|| (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false))
|
||||
) {
|
||||
$createDefaultPackagistRepo = false;
|
||||
continue;
|
||||
}
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, $repoConfig);
|
||||
}
|
||||
|
||||
if ($createDefaultPackagistRepo) {
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, array(
|
||||
'type' => 'composer',
|
||||
'url' => 'https://packagist.org',
|
||||
'url' => 'https://repo.packagist.org',
|
||||
));
|
||||
}
|
||||
|
||||
$this->repos = new CompositeRepository($repos);
|
||||
unset($repos, $config, $repositories);
|
||||
|
@ -203,11 +223,11 @@ EOT
|
|||
$name = get_current_user() . '/' . $name;
|
||||
} else {
|
||||
// package names must be in the format foo/bar
|
||||
$name = $name . '/' . $name;
|
||||
$name .= '/' . $name;
|
||||
}
|
||||
$name = strtolower($name);
|
||||
} else {
|
||||
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $name)) {
|
||||
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $name)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The package name '.$name.' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
|
||||
);
|
||||
|
@ -221,7 +241,7 @@ EOT
|
|||
return $name;
|
||||
}
|
||||
|
||||
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $value)) {
|
||||
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The package name '.$value.' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
|
||||
);
|
||||
|
@ -279,7 +299,7 @@ EOT
|
|||
$minimumStability = $input->getOption('stability') ?: null;
|
||||
$minimumStability = $io->askAndValidate(
|
||||
'Minimum Stability [<comment>'.$minimumStability.'</comment>]: ',
|
||||
function ($value) use ($self, $minimumStability) {
|
||||
function ($value) use ($minimumStability) {
|
||||
if (null === $value) {
|
||||
return $minimumStability;
|
||||
}
|
||||
|
@ -319,11 +339,16 @@ EOT
|
|||
|
||||
$io->writeError(array('', 'Define your dependencies.', ''));
|
||||
|
||||
// prepare to resolve dependencies
|
||||
$repos = $this->getRepos();
|
||||
$preferredStability = $minimumStability ?: 'stable';
|
||||
$phpVersion = $repos->findPackage('php', '*')->getPrettyVersion();
|
||||
|
||||
$question = 'Would you like to define your dependencies (require) interactively [<comment>yes</comment>]? ';
|
||||
$require = $input->getOption('require');
|
||||
$requirements = array();
|
||||
if ($require || $io->askConfirmation($question, true)) {
|
||||
$requirements = $this->determineRequirements($input, $output, $require);
|
||||
$requirements = $this->determineRequirements($input, $output, $require, $phpVersion, $preferredStability);
|
||||
}
|
||||
$input->setOption('require', $requirements);
|
||||
|
||||
|
@ -331,7 +356,7 @@ EOT
|
|||
$requireDev = $input->getOption('require-dev');
|
||||
$devRequirements = array();
|
||||
if ($requireDev || $io->askConfirmation($question, true)) {
|
||||
$devRequirements = $this->determineRequirements($input, $output, $requireDev);
|
||||
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $phpVersion, $preferredStability);
|
||||
}
|
||||
$input->setOption('require-dev', $devRequirements);
|
||||
}
|
||||
|
@ -375,7 +400,7 @@ EOT
|
|||
return $this->repos;
|
||||
}
|
||||
|
||||
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable')
|
||||
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true)
|
||||
{
|
||||
if ($requires) {
|
||||
$requires = $this->normalizeRequirements($requires);
|
||||
|
@ -385,9 +410,12 @@ EOT
|
|||
foreach ($requires as $requirement) {
|
||||
if (!isset($requirement['version'])) {
|
||||
// determine the best version automatically
|
||||
$version = $this->findBestVersionForPackage($input, $requirement['name'], $phpVersion, $preferredStability);
|
||||
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability);
|
||||
$requirement['version'] = $version;
|
||||
|
||||
// replace package name from packagist.org
|
||||
$requirement['name'] = $name;
|
||||
|
||||
$io->writeError(sprintf(
|
||||
'Using version <info>%s</info> for <info>%s</info>',
|
||||
$requirement['version'],
|
||||
|
@ -395,7 +423,10 @@ EOT
|
|||
));
|
||||
} else {
|
||||
// check that the specified version/constraint exists before we proceed
|
||||
$this->findBestVersionForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $requirement['version'], 'dev');
|
||||
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev');
|
||||
|
||||
// replace package name from packagist.org
|
||||
$requirement['name'] = $name;
|
||||
}
|
||||
|
||||
$result[] = $requirement['name'] . ' ' . $requirement['version'];
|
||||
|
@ -493,7 +524,7 @@ EOT
|
|||
);
|
||||
|
||||
if (false === $constraint) {
|
||||
$constraint = $this->findBestVersionForPackage($input, $package, $phpVersion, $preferredStability);
|
||||
list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $phpVersion, $preferredStability);
|
||||
|
||||
$io->writeError(sprintf(
|
||||
'Using version <info>%s</info> for <info>%s</info>',
|
||||
|
@ -539,7 +570,12 @@ EOT
|
|||
$finder = new ExecutableFinder();
|
||||
$gitBin = $finder->find('git');
|
||||
|
||||
// TODO in v3 always call with an array
|
||||
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
|
||||
$cmd = new Process(array($gitBin, 'config', '-l'));
|
||||
} else {
|
||||
$cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin)));
|
||||
}
|
||||
$cmd->run();
|
||||
|
||||
if ($cmd->isSuccessful()) {
|
||||
|
@ -625,16 +661,16 @@ EOT
|
|||
return false !== filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
private function getPool(InputInterface $input, $minimumStability = null)
|
||||
private function getRepositorySet(InputInterface $input, $minimumStability = null)
|
||||
{
|
||||
$key = $minimumStability ?: 'default';
|
||||
|
||||
if (!isset($this->pools[$key])) {
|
||||
$this->pools[$key] = $pool = new Pool($minimumStability ?: $this->getMinimumStability($input));
|
||||
$pool->addRepository($this->getRepos());
|
||||
if (!isset($this->repositorySets[$key])) {
|
||||
$this->repositorySets[$key] = $repositorySet = new RepositorySet(array(), $minimumStability ?: $this->getMinimumStability($input));
|
||||
$repositorySet->addRepository($this->getRepos());
|
||||
}
|
||||
|
||||
return $this->pools[$key];
|
||||
return $this->repositorySets[$key];
|
||||
}
|
||||
|
||||
private function getMinimumStability(InputInterface $input)
|
||||
|
@ -660,40 +696,71 @@ EOT
|
|||
*
|
||||
* @param InputInterface $input
|
||||
* @param string $name
|
||||
* @param string $phpVersion
|
||||
* @param string|null $phpVersion
|
||||
* @param string $preferredStability
|
||||
* @param string|null $requiredVersion
|
||||
* @param string $minimumStability
|
||||
* @throws \InvalidArgumentException
|
||||
* @return string
|
||||
* @return array name version
|
||||
*/
|
||||
private function findBestVersionForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null)
|
||||
private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null)
|
||||
{
|
||||
// find the latest version allowed in this pool
|
||||
$versionSelector = new VersionSelector($this->getPool($input, $minimumStability));
|
||||
// find the latest version allowed in this repo set
|
||||
$versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability));
|
||||
$ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs');
|
||||
|
||||
// ignore phpVersion if platform requirements are ignored
|
||||
if ($ignorePlatformReqs) {
|
||||
$phpVersion = null;
|
||||
}
|
||||
|
||||
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
|
||||
|
||||
if (!$package) {
|
||||
// platform packages can not be found in the pool in versions other than the local platform's has
|
||||
// so if platform reqs are ignored we just take the user's word for it
|
||||
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
|
||||
return array($name, $requiredVersion ?: '*');
|
||||
}
|
||||
|
||||
// Check whether the PHP version was the problem
|
||||
if ($phpVersion && $versionSelector->findBestCandidate($name, $requiredVersion, null, $preferredStability)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)', $name, $requiredVersion, $phpVersion
|
||||
'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)',
|
||||
$name,
|
||||
$requiredVersion,
|
||||
$phpVersion
|
||||
));
|
||||
}
|
||||
// Check whether the required version was the problem
|
||||
if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $phpVersion, $preferredStability)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Could not find package %s in a version matching %s', $name, $requiredVersion
|
||||
'Could not find package %s in a version matching %s',
|
||||
$name,
|
||||
$requiredVersion
|
||||
));
|
||||
}
|
||||
// Check whether the PHP version was the problem
|
||||
if ($phpVersion && $versionSelector->findBestCandidate($name)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Could not find package %s in any version matching your PHP version (%s)', $name, $phpVersion
|
||||
'Could not find package %s in any version matching your PHP version (%s)',
|
||||
$name,
|
||||
$phpVersion
|
||||
));
|
||||
}
|
||||
|
||||
// Check for similar names/typos
|
||||
$similar = $this->findSimilar($name);
|
||||
if ($similar) {
|
||||
// Check whether the minimum stability was the problem but the package exists
|
||||
if ($requiredVersion === null && in_array($name, $similar, true)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.',
|
||||
$name,
|
||||
$this->getMinimumStability($input)
|
||||
));
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
"Could not find package %s.\n\nDid you mean " . (count($similar) > 1 ? 'one of these' : 'this') . "?\n %s",
|
||||
$name,
|
||||
|
@ -708,7 +775,10 @@ EOT
|
|||
));
|
||||
}
|
||||
|
||||
return $versionSelector->findRecommendedRequireVersion($package);
|
||||
return array(
|
||||
$package->getPrettyName(),
|
||||
$versionSelector->findRecommendedRequireVersion($package),
|
||||
);
|
||||
}
|
||||
|
||||
private function findSimilar($package)
|
||||
|
@ -728,4 +798,23 @@ EOT
|
|||
|
||||
return array_keys(array_slice($similarPackages, 0, 5));
|
||||
}
|
||||
|
||||
private function installDependencies($output)
|
||||
{
|
||||
try {
|
||||
$installCommand = $this->getApplication()->find('install');
|
||||
$installCommand->run(new ArrayInput(array()), $output);
|
||||
} catch (\Exception $e) {
|
||||
$this->getIO()->writeError('Could not install dependencies. Run `composer install` to see more information.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function hasDependencies($options)
|
||||
{
|
||||
$requires = (array) $options['require'];
|
||||
$devRequires = isset($options['require-dev']) ? (array) $options['require-dev'] : array();
|
||||
|
||||
return !empty($requires) || !empty($devRequires);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ class InstallCommand extends BaseCommand
|
|||
{
|
||||
$this
|
||||
->setName('install')
|
||||
->setAliases(array('i'))
|
||||
->setDescription('Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.')
|
||||
->setDefinition(array(
|
||||
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
|
||||
|
@ -51,7 +52,8 @@ class InstallCommand extends BaseCommand
|
|||
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>install</info> command reads the composer.lock file from
|
||||
the current directory, processes it, and downloads and installs all the
|
||||
libraries and dependencies outlined in that file. If the file does not
|
||||
|
@ -59,6 +61,7 @@ exist it will look for composer.json and do the same.
|
|||
|
||||
<info>php composer.phar install</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#install-i
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -83,7 +86,6 @@ EOT
|
|||
}
|
||||
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
|
|
@ -36,10 +36,12 @@ class LicensesCommand extends BaseCommand
|
|||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
|
||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The license command displays detailed information about the licenses of
|
||||
the installed dependencies.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#licenses
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -74,8 +76,9 @@ EOT
|
|||
|
||||
$table = new Table($output);
|
||||
$table->setStyle('compact');
|
||||
$table->getStyle()->setVerticalBorderChar('');
|
||||
$table->getStyle()->setCellRowContentFormat('%s ');
|
||||
$tableStyle = $table->getStyle();
|
||||
$tableStyle->setVerticalBorderChar('');
|
||||
$tableStyle->setCellRowContentFormat('%s ');
|
||||
$table->setHeaders(array('Name', 'Version', 'License'));
|
||||
foreach ($packages as $package) {
|
||||
$table->addRow(array(
|
||||
|
|
|
@ -36,8 +36,10 @@ class OutdatedCommand extends ShowCommand
|
|||
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'),
|
||||
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --outdated option.'),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
|
||||
new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it with the --outdated option if you don\'t want to be informed about new versions of some packages.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The outdated command is just a proxy for `composer show -l`
|
||||
|
||||
The color coding (or signage if you have ANSI colors disabled) for dependency versions is as such:
|
||||
|
@ -48,7 +50,7 @@ The color coding (or signage if you have ANSI colors disabled) for dependency ve
|
|||
may involve work.
|
||||
- <highlight>red</highlight> (!): Dependency has a new version that is semver-compatible and you should upgrade it.
|
||||
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#outdated
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -76,6 +78,7 @@ EOT
|
|||
$args['--minor-only'] = true;
|
||||
}
|
||||
$args['--format'] = $input->getOption('format');
|
||||
$args['--ignore'] = $input->getOption('ignore');
|
||||
|
||||
$input = new ArrayInput($args);
|
||||
|
||||
|
|
|
@ -31,11 +31,13 @@ class ProhibitsCommand extends BaseDependencyCommand
|
|||
->setName('prohibits')
|
||||
->setAliases(array('why-not'))
|
||||
->setDescription('Shows which packages prevent the given package from being installed.')
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
Displays detailed information about why a package cannot be installed.
|
||||
|
||||
<info>php composer.phar prohibits composer/composer</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#prohibits-why-not-
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
|
|
@ -22,6 +22,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Composer\Package\BasePackage;
|
||||
|
||||
/**
|
||||
* @author Pierre du Plessis <pdples@gmail.com>
|
||||
|
@ -48,12 +49,14 @@ class RemoveCommand extends BaseCommand
|
|||
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
|
||||
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>remove</info> command removes a package from the current
|
||||
list of installed packages
|
||||
|
||||
<info>php composer.phar remove</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#remove
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -99,6 +102,19 @@ EOT
|
|||
$json->removeLink($altType, $composer[$altType][$package]);
|
||||
}
|
||||
}
|
||||
} elseif (isset($composer[$type]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$type]))) {
|
||||
foreach ($matches as $matchedPackage) {
|
||||
$json->removeLink($type, $matchedPackage);
|
||||
}
|
||||
} elseif (isset($composer[$altType]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$altType]))) {
|
||||
foreach ($matches as $matchedPackage) {
|
||||
$io->writeError('<warning>' . $matchedPackage . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>');
|
||||
if ($io->isInteractive()) {
|
||||
if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) {
|
||||
$json->removeLink($altType, $matchedPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$io->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
|
||||
}
|
||||
|
@ -111,7 +127,6 @@ EOT
|
|||
// Update packages
|
||||
$this->resetComposer();
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
|
|
@ -25,6 +25,8 @@ use Composer\Plugin\CommandEvent;
|
|||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Silencer;
|
||||
|
||||
/**
|
||||
* @author Jérémy Romey <jeremy@free-agent.fr>
|
||||
|
@ -32,6 +34,11 @@ use Composer\Repository\PlatformRepository;
|
|||
*/
|
||||
class RequireCommand extends InitCommand
|
||||
{
|
||||
private $newlyCreated;
|
||||
private $json;
|
||||
private $file;
|
||||
private $composerBackup;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
|
@ -57,7 +64,8 @@ class RequireCommand extends InitCommand
|
|||
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
|
||||
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The require command adds required packages to your composer.json and installs them.
|
||||
|
||||
If you do not specify a package, composer will prompt you to search for a package, and given results, provide a list of
|
||||
|
@ -67,6 +75,7 @@ If you do not specify a version constraint, composer will choose a suitable one
|
|||
|
||||
If you do not want to install the new dependencies immediately you can call it with --no-update
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#require
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -74,32 +83,42 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$file = Factory::getComposerFile();
|
||||
if (function_exists('pcntl_async_signals')) {
|
||||
pcntl_async_signals(true);
|
||||
pcntl_signal(SIGINT, array($this, 'revertComposerFile'));
|
||||
pcntl_signal(SIGTERM, array($this, 'revertComposerFile'));
|
||||
pcntl_signal(SIGHUP, array($this, 'revertComposerFile'));
|
||||
}
|
||||
|
||||
$this->file = Factory::getComposerFile();
|
||||
$io = $this->getIO();
|
||||
|
||||
$newlyCreated = !file_exists($file);
|
||||
if ($newlyCreated && !file_put_contents($file, "{\n}\n")) {
|
||||
$io->writeError('<error>'.$file.' could not be created.</error>');
|
||||
$this->newlyCreated = !file_exists($this->file);
|
||||
if ($this->newlyCreated && !file_put_contents($this->file, "{\n}\n")) {
|
||||
$io->writeError('<error>'.$this->file.' could not be created.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (!is_readable($file)) {
|
||||
$io->writeError('<error>'.$file.' is not readable.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (!is_writable($file)) {
|
||||
$io->writeError('<error>'.$file.' is not writable.</error>');
|
||||
if (!is_readable($this->file)) {
|
||||
$io->writeError('<error>'.$this->file.' is not readable.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (filesize($file) === 0) {
|
||||
file_put_contents($file, "{\n}\n");
|
||||
if (filesize($this->file) === 0) {
|
||||
file_put_contents($this->file, "{\n}\n");
|
||||
}
|
||||
|
||||
$json = new JsonFile($file);
|
||||
$composerBackup = file_get_contents($json->getPath());
|
||||
$this->json = new JsonFile($this->file);
|
||||
$this->composerBackup = file_get_contents($this->json->getPath());
|
||||
|
||||
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
|
||||
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
|
||||
if (!is_writable($this->file) && !Silencer::call('file_put_contents', $this->file, $this->composerBackup)) {
|
||||
$io->writeError('<error>'.$this->file.' is not writable.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$repos = $composer->getRepositoryManager()->getRepositories();
|
||||
|
@ -118,7 +137,7 @@ EOT
|
|||
}
|
||||
|
||||
$phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion();
|
||||
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability);
|
||||
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'));
|
||||
|
||||
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
|
||||
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
|
||||
|
@ -126,36 +145,51 @@ EOT
|
|||
|
||||
// validate requirements format
|
||||
$versionParser = new VersionParser();
|
||||
foreach ($requirements as $constraint) {
|
||||
foreach ($requirements as $package => $constraint) {
|
||||
if (strtolower($package) === $composer->getPackage()->getName()) {
|
||||
$io->writeError(sprintf('<error>Root package \'%s\' cannot require itself in its composer.json</error>', $package));
|
||||
|
||||
return 1;
|
||||
}
|
||||
$versionParser->parseConstraints($constraint);
|
||||
}
|
||||
|
||||
$sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages');
|
||||
|
||||
if (!$this->updateFileCleanly($json, $requirements, $requireKey, $removeKey, $sortPackages)) {
|
||||
$composerDefinition = $json->read();
|
||||
if (!$this->updateFileCleanly($this->json, $requirements, $requireKey, $removeKey, $sortPackages)) {
|
||||
$composerDefinition = $this->json->read();
|
||||
foreach ($requirements as $package => $version) {
|
||||
$composerDefinition[$requireKey][$package] = $version;
|
||||
unset($composerDefinition[$removeKey][$package]);
|
||||
}
|
||||
$json->write($composerDefinition);
|
||||
$this->json->write($composerDefinition);
|
||||
}
|
||||
|
||||
$io->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
|
||||
$io->writeError('<info>'.$this->file.' has been '.($this->newlyCreated ? 'created' : 'updated').'</info>');
|
||||
|
||||
if ($input->getOption('no-update')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->doUpdate($input, $output, $io, $requirements);
|
||||
} catch (\Exception $e) {
|
||||
$this->revertComposerFile(false);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function doUpdate(InputInterface $input, OutputInterface $output, IOInterface $io, array $requirements)
|
||||
{
|
||||
// Update packages
|
||||
$this->resetComposer();
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
|
||||
$updateDevMode = !$input->getOption('update-no-dev');
|
||||
$optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
|
||||
$authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
|
||||
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
|
||||
|
||||
// Update packages
|
||||
$this->resetComposer();
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
|
@ -182,13 +216,7 @@ EOT
|
|||
|
||||
$status = $install->run();
|
||||
if ($status !== 0) {
|
||||
if ($newlyCreated) {
|
||||
$io->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
|
||||
unlink($json->getPath());
|
||||
} else {
|
||||
$io->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
|
||||
file_put_contents($json->getPath(), $composerBackup);
|
||||
}
|
||||
$this->revertComposerFile(false);
|
||||
}
|
||||
|
||||
return $status;
|
||||
|
@ -218,4 +246,21 @@ EOT
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public function revertComposerFile($hardExit = true)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
if ($this->newlyCreated) {
|
||||
$io->writeError("\n".'<error>Installation failed, deleting '.$this->file.'.</error>');
|
||||
unlink($this->json->getPath());
|
||||
} else {
|
||||
$io->writeError("\n".'<error>Installation failed, reverting '.$this->file.' to its original content.</error>');
|
||||
file_put_contents($this->json->getPath(), $this->composerBackup);
|
||||
}
|
||||
|
||||
if ($hardExit) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien.potencier@gmail.com>
|
||||
|
@ -47,6 +48,7 @@ class RunScriptCommand extends BaseCommand
|
|||
{
|
||||
$this
|
||||
->setName('run-script')
|
||||
->setAliases(array('run'))
|
||||
->setDescription('Runs the scripts defined in composer.json.')
|
||||
->setDefinition(array(
|
||||
new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.'),
|
||||
|
@ -56,10 +58,13 @@ class RunScriptCommand extends BaseCommand
|
|||
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:
|
||||
|
||||
<info>php composer.phar run-script post-update-cmd</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#run-script
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -68,7 +73,7 @@ EOT
|
|||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($input->getOption('list')) {
|
||||
return $this->listScripts();
|
||||
return $this->listScripts($output);
|
||||
} elseif (!$input->getArgument('script')) {
|
||||
throw new \RuntimeException('Missing required argument "script"');
|
||||
}
|
||||
|
@ -101,7 +106,7 @@ EOT
|
|||
return $composer->getEventDispatcher()->dispatchScript($script, $devMode, $args);
|
||||
}
|
||||
|
||||
protected function listScripts()
|
||||
protected function listScripts(OutputInterface $output)
|
||||
{
|
||||
$scripts = $this->getComposer()->getPackage()->getScripts();
|
||||
|
||||
|
@ -111,9 +116,26 @@ EOT
|
|||
|
||||
$io = $this->getIO();
|
||||
$io->writeError('<info>scripts:</info>');
|
||||
$table = array();
|
||||
foreach ($scripts as $name => $script) {
|
||||
$io->write(' ' . $name);
|
||||
$description = '';
|
||||
try {
|
||||
$cmd = $this->getApplication()->find($name);
|
||||
if ($cmd instanceof ScriptAliasCommand) {
|
||||
$description = $cmd->getDescription();
|
||||
}
|
||||
} catch (\Symfony\Component\Console\Exception\CommandNotFoundException $e) {
|
||||
// ignore scripts that have no command associated, like native Composer script listeners
|
||||
}
|
||||
$table[] = array(' '.$name, $description);
|
||||
}
|
||||
|
||||
$renderer = new Table($output);
|
||||
$renderer->setStyle('compact');
|
||||
$rendererStyle = $renderer->getStyle();
|
||||
$rendererStyle->setVerticalBorderChar('');
|
||||
$rendererStyle->setCellRowContentFormat('%s ');
|
||||
$renderer->setRows($table)->render();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -43,10 +43,13 @@ class ScriptAliasCommand extends BaseCommand
|
|||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
|
||||
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>run-script</info> command runs scripts defined in composer.json:
|
||||
|
||||
<info>php composer.phar run-script post-update-cmd</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#run-script
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
|
|
@ -44,10 +44,12 @@ class SearchCommand extends BaseCommand
|
|||
new InputOption('type', 't', InputOption::VALUE_REQUIRED, 'Search for a specific package type'),
|
||||
new InputArgument('tokens', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'tokens to search for'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The search command searches for packages by its name
|
||||
<info>php composer.phar search symfony composer</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#search
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -59,7 +61,7 @@ EOT
|
|||
$platformRepo = new PlatformRepository;
|
||||
$io = $this->getIO();
|
||||
if (!($composer = $this->getComposer(false))) {
|
||||
$composer = Factory::create($this->getIO(), array());
|
||||
$composer = Factory::create($this->getIO(), array(), $input->hasParameterOption('--no-plugins'));
|
||||
}
|
||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
||||
|
|
|
@ -53,12 +53,14 @@ class SelfUpdateCommand extends BaseCommand
|
|||
new InputOption('snapshot', null, InputOption::VALUE_NONE, 'Force an update to the snapshot channel'),
|
||||
new InputOption('set-channel-only', null, InputOption::VALUE_NONE, 'Only store the channel as the default one and then exit'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>self-update</info> command checks getcomposer.org for newer
|
||||
versions of composer and if found, installs the latest.
|
||||
|
||||
<info>php composer.phar self-update</info>
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#self-update-selfupdate-
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -75,9 +77,9 @@ EOT
|
|||
}
|
||||
|
||||
$io = $this->getIO();
|
||||
$remoteFilesystem = Factory::createRemoteFilesystem($io, $config);
|
||||
$httpDownloader = Factory::createHttpDownloader($io, $config);
|
||||
|
||||
$versionsUtil = new Versions($config, $remoteFilesystem);
|
||||
$versionsUtil = new Versions($config, $httpDownloader);
|
||||
|
||||
// switch channel if requested
|
||||
foreach (array('stable', 'preview', 'snapshot') as $channel) {
|
||||
|
@ -107,6 +109,15 @@ EOT
|
|||
throw new FilesystemException('Composer update failed: the "'.$tmpDir.'" directory used to download the temp file could not be written');
|
||||
}
|
||||
|
||||
// check if composer is running as the same user that owns the directory root, only if POSIX is defined and callable
|
||||
if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
|
||||
$composeUser = posix_getpwuid(posix_geteuid());
|
||||
$homeOwner = posix_getpwuid(fileowner($home));
|
||||
if (isset($composeUser['name']) && isset($homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) {
|
||||
$io->writeError('<warning>You are running composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"</warning>');
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('rollback')) {
|
||||
return $this->rollback($output, $rollbackDir, $localFilename);
|
||||
}
|
||||
|
@ -145,9 +156,9 @@ EOT
|
|||
|
||||
$io->write(sprintf("Updating to version <info>%s</info> (%s channel).", $updateVersion, $versionsUtil->getChannel()));
|
||||
$remoteFilename = $baseUrl . ($updatingToTag ? "/download/{$updateVersion}/composer.phar" : '/composer.phar');
|
||||
$signature = $remoteFilesystem->getContents(self::HOMEPAGE, $remoteFilename.'.sig', false);
|
||||
$signature = $httpDownloader->get($remoteFilename.'.sig')->getBody();
|
||||
$io->writeError(' ', false);
|
||||
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
|
||||
$httpDownloader->copy($remoteFilename, $tempFilename);
|
||||
$io->writeError('');
|
||||
|
||||
if (!file_exists($tempFilename) || !$signature) {
|
||||
|
@ -167,7 +178,9 @@ EOT
|
|||
|
||||
$sigFile = 'file://'.$home.'/' . ($updatingToTag ? 'keys.tags.pub' : 'keys.dev.pub');
|
||||
if (!file_exists($sigFile)) {
|
||||
file_put_contents($home.'/keys.dev.pub', <<<DEVPUBKEY
|
||||
file_put_contents(
|
||||
$home.'/keys.dev.pub',
|
||||
<<<DEVPUBKEY
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnBDHjZS6e0ZMoK3xTD7f
|
||||
FNCzlXjX/Aie2dit8QXA03pSrOTbaMnxON3hUL47Lz3g1SC6YJEMVHr0zYq4elWi
|
||||
|
@ -184,7 +197,10 @@ wSEuAuRm+pRqi8BRnQ/GKUcCAwEAAQ==
|
|||
-----END PUBLIC KEY-----
|
||||
DEVPUBKEY
|
||||
);
|
||||
file_put_contents($home.'/keys.tags.pub', <<<TAGSPUBKEY
|
||||
|
||||
file_put_contents(
|
||||
$home.'/keys.tags.pub',
|
||||
<<<TAGSPUBKEY
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0Vi/2K6apCVj76nCnCl2
|
||||
MQUPdK+A9eqkYBacXo2wQBYmyVlXm2/n/ZsX6pCLYPQTHyr5jXbkQzBw8SKqPdlh
|
||||
|
@ -205,7 +221,7 @@ TAGSPUBKEY
|
|||
|
||||
$pubkeyid = openssl_pkey_get_public($sigFile);
|
||||
$algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384';
|
||||
if (!in_array('SHA384', openssl_get_md_methods())) {
|
||||
if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) {
|
||||
throw new \RuntimeException('SHA384 is not supported by your openssl extension, could not verify the phar file integrity');
|
||||
}
|
||||
$signature = json_decode($signature, true);
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace Composer\Command;
|
|||
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\DefaultPolicy;
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\CompletePackageInterface;
|
||||
|
@ -29,6 +28,7 @@ use Composer\Repository\CompositeRepository;
|
|||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositoryFactory;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Semver;
|
||||
use Composer\Spdx\SpdxLicenses;
|
||||
|
@ -52,8 +52,8 @@ class ShowCommand extends BaseCommand
|
|||
protected $versionParser;
|
||||
protected $colors;
|
||||
|
||||
/** @var Pool */
|
||||
private $pool;
|
||||
/** @var RepositorySet */
|
||||
private $repositorySet;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
|
@ -74,15 +74,18 @@ class ShowCommand extends BaseCommand
|
|||
new InputOption('tree', 't', InputOption::VALUE_NONE, 'List the dependencies as a tree'),
|
||||
new InputOption('latest', 'l', InputOption::VALUE_NONE, 'Show the latest version'),
|
||||
new InputOption('outdated', 'o', InputOption::VALUE_NONE, 'Show the latest version but only for packages that are outdated'),
|
||||
new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it with the --outdated option if you don\'t want to be informed about new versions of some packages.'),
|
||||
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --outdated option.'),
|
||||
new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'),
|
||||
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The show command displays detailed information about a package, or
|
||||
lists all packages available.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#show
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -104,6 +107,8 @@ EOT
|
|||
|
||||
if ($input->getOption('outdated')) {
|
||||
$input->setOption('latest', true);
|
||||
} elseif ($input->getOption('ignore')) {
|
||||
$io->writeError('<warning>You are using the option "ignore" for action other than "outdated", it will be ignored.</warning>');
|
||||
}
|
||||
|
||||
if ($input->getOption('direct') && ($input->getOption('all') || $input->getOption('available') || $input->getOption('platform'))) {
|
||||
|
@ -118,6 +123,12 @@ EOT
|
|||
return 1;
|
||||
}
|
||||
|
||||
if ($input->getOption('tree') && $input->getOption('latest')) {
|
||||
$io->writeError('The --tree (-t) option is not usable in combination with --latest (-l)');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$format = $input->getOption('format');
|
||||
if (!in_array($format, array('text', 'json'))) {
|
||||
$io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format));
|
||||
|
@ -178,9 +189,6 @@ EOT
|
|||
|
||||
// show single package or single version
|
||||
if (($packageFilter && false === strpos($packageFilter, '*')) || !empty($package)) {
|
||||
if ('json' === $format) {
|
||||
$io->writeError('Format "json" is only supported for package listings, falling back to format "text"');
|
||||
}
|
||||
if (empty($package)) {
|
||||
list($package, $versions) = $this->getPackage($installedRepo, $repos, $input->getArgument('package'), $input->getArgument('version'));
|
||||
|
||||
|
@ -200,7 +208,13 @@ EOT
|
|||
|
||||
$exitCode = 0;
|
||||
if ($input->getOption('tree')) {
|
||||
$this->displayPackageTree($package, $installedRepo, $repos);
|
||||
$arrayTree = $this->generatePackageTree($package, $installedRepo, $repos);
|
||||
|
||||
if ('json' === $format) {
|
||||
$io->write(JsonFile::encode(array('installed' => array($arrayTree))));
|
||||
} else {
|
||||
$this->displayPackageTree(array($arrayTree));
|
||||
}
|
||||
} else {
|
||||
$latestPackage = null;
|
||||
if ($input->getOption('latest')) {
|
||||
|
@ -209,6 +223,12 @@ EOT
|
|||
if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
if ($input->getOption('path')) {
|
||||
$io->write($package->getName(), false);
|
||||
$io->write(' ' . strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n"));
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
$this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null);
|
||||
$this->printLinks($package, 'requires');
|
||||
$this->printLinks($package, 'devRequires', 'requires (dev)');
|
||||
|
@ -228,18 +248,22 @@ EOT
|
|||
|
||||
// show tree view if requested
|
||||
if ($input->getOption('tree')) {
|
||||
if ('json' === $format) {
|
||||
$io->writeError('Format "json" is only supported for package listings, falling back to format "text"');
|
||||
}
|
||||
$rootRequires = $this->getRootRequires();
|
||||
$packages = $installedRepo->getPackages();
|
||||
usort($packages, 'strcmp');
|
||||
$arrayTree = array();
|
||||
foreach ($packages as $package) {
|
||||
if (in_array($package->getName(), $rootRequires, true)) {
|
||||
$this->displayPackageTree($package, $installedRepo, $repos);
|
||||
$arrayTree[] = $this->generatePackageTree($package, $installedRepo, $repos);
|
||||
}
|
||||
}
|
||||
|
||||
if ('json' === $format) {
|
||||
$io->write(JsonFile::encode(array('installed' => $arrayTree)));
|
||||
} else {
|
||||
$this->displayPackageTree($arrayTree);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -294,8 +318,8 @@ EOT
|
|||
} else {
|
||||
$type = 'available';
|
||||
}
|
||||
if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
|
||||
foreach ($repo->getProviderNames() as $name) {
|
||||
if ($repo instanceof ComposerRepository) {
|
||||
foreach ($repo->getPackageNames() as $name) {
|
||||
if (!$packageFilter || preg_match($packageFilter, $name)) {
|
||||
$packages[$type][$name] = $name;
|
||||
}
|
||||
|
@ -319,6 +343,7 @@ EOT
|
|||
$showAllTypes = $input->getOption('all');
|
||||
$showLatest = $input->getOption('latest');
|
||||
$showMinorOnly = $input->getOption('minor-only');
|
||||
$ignoredPackages = array_map('strtolower', $input->getOption('ignore'));
|
||||
$indent = $showAllTypes ? ' ' : '';
|
||||
$latestPackages = array();
|
||||
$exitCode = 0;
|
||||
|
@ -329,24 +354,18 @@ EOT
|
|||
ksort($packages[$type]);
|
||||
|
||||
$nameLength = $versionLength = $latestLength = 0;
|
||||
|
||||
if ($showLatest && $showVersion) {
|
||||
foreach ($packages[$type] as $package) {
|
||||
if (is_object($package)) {
|
||||
$nameLength = max($nameLength, strlen($package->getPrettyName()));
|
||||
if ($showVersion) {
|
||||
$versionLength = max($versionLength, strlen($package->getFullPrettyVersion()));
|
||||
if ($showLatest) {
|
||||
$latestPackage = $this->findLatestPackage($package, $composer, $phpVersion, $showMinorOnly);
|
||||
if ($latestPackage === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestPackages[$package->getPrettyName()] = $latestPackage;
|
||||
$latestLength = max($latestLength, strlen($latestPackage->getFullPrettyVersion()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$nameLength = max($nameLength, strlen($package));
|
||||
}
|
||||
}
|
||||
|
||||
$writePath = !$input->getOption('name-only') && $input->getOption('path');
|
||||
|
@ -357,11 +376,6 @@ EOT
|
|||
$hasOutdatedPackages = false;
|
||||
|
||||
$viewData[$type] = array();
|
||||
$viewMetaData[$type] = array(
|
||||
'nameLength' => $nameLength,
|
||||
'versionLength' => $versionLength,
|
||||
'latestLength' => $latestLength,
|
||||
);
|
||||
foreach ($packages[$type] as $package) {
|
||||
$packageViewData = array();
|
||||
if (is_object($package)) {
|
||||
|
@ -369,19 +383,26 @@ EOT
|
|||
if ($showLatest && isset($latestPackages[$package->getPrettyName()])) {
|
||||
$latestPackage = $latestPackages[$package->getPrettyName()];
|
||||
}
|
||||
if ($input->getOption('outdated') && $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) {
|
||||
|
||||
// Determine if Composer is checking outdated dependencies and if current package should trigger non-default exit code
|
||||
$packageIsUpToDate = $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && !$latestPackage->isAbandoned();
|
||||
$packageIsIgnored = \in_array($package->getPrettyName(), $ignoredPackages, true);
|
||||
if ($input->getOption('outdated') && ($packageIsUpToDate || $packageIsIgnored)) {
|
||||
continue;
|
||||
} elseif ($input->getOption('outdated') || $input->getOption('strict')) {
|
||||
$hasOutdatedPackages = true;
|
||||
}
|
||||
|
||||
$packageViewData['name'] = $package->getPrettyName();
|
||||
$nameLength = max($nameLength, strlen($package->getPrettyName()));
|
||||
if ($writeVersion) {
|
||||
$packageViewData['version'] = $package->getFullPrettyVersion();
|
||||
$versionLength = max($versionLength, strlen($package->getFullPrettyVersion()));
|
||||
}
|
||||
if ($writeLatest && $latestPackage) {
|
||||
$packageViewData['latest'] = $latestPackage->getFullPrettyVersion();
|
||||
$packageViewData['latest-status'] = $this->getUpdateStatus($latestPackage, $package);
|
||||
$latestLength = max($latestLength, strlen($latestPackage->getFullPrettyVersion()));
|
||||
}
|
||||
if ($writeDescription) {
|
||||
$packageViewData['description'] = $package->getDescription();
|
||||
|
@ -391,7 +412,7 @@ EOT
|
|||
}
|
||||
|
||||
if ($latestPackage && $latestPackage->isAbandoned()) {
|
||||
$replacement = (is_string($latestPackage->getReplacementPackage()))
|
||||
$replacement = is_string($latestPackage->getReplacementPackage())
|
||||
? 'Use ' . $latestPackage->getReplacementPackage() . ' instead'
|
||||
: 'No replacement was suggested';
|
||||
$packageWarning = sprintf(
|
||||
|
@ -403,9 +424,15 @@ EOT
|
|||
}
|
||||
} else {
|
||||
$packageViewData['name'] = $package;
|
||||
$nameLength = max($nameLength, strlen($package));
|
||||
}
|
||||
$viewData[$type][] = $packageViewData;
|
||||
}
|
||||
$viewMetaData[$type] = array(
|
||||
'nameLength' => $nameLength,
|
||||
'versionLength' => $versionLength,
|
||||
'latestLength' => $latestLength,
|
||||
);
|
||||
if ($input->getOption('strict') && $hasOutdatedPackages) {
|
||||
$exitCode = 1;
|
||||
break;
|
||||
|
@ -467,7 +494,7 @@ EOT
|
|||
}
|
||||
$io->write('');
|
||||
if (isset($package['warning'])) {
|
||||
$io->write('<warning>' . $package['warning'] . '</warning>');
|
||||
$io->writeError('<warning>' . $package['warning'] . '</warning>');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,19 +538,13 @@ EOT
|
|||
$constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version;
|
||||
|
||||
$policy = new DefaultPolicy();
|
||||
$pool = new Pool('dev');
|
||||
$pool->addRepository($repos);
|
||||
$repositorySet = new RepositorySet(array(), 'dev');
|
||||
$repositorySet->addRepository($repos);
|
||||
|
||||
$matchedPackage = null;
|
||||
$versions = array();
|
||||
$matches = $pool->whatProvides($name, $constraint);
|
||||
$matches = $repositorySet->findPackages($name, $constraint);
|
||||
foreach ($matches as $index => $package) {
|
||||
// skip providers/replacers
|
||||
if ($package->getName() !== $name) {
|
||||
unset($matches[$index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// select an exact match if it is in the installed repo and no specific version was required
|
||||
if (null === $version && $installedRepo->hasPackage($package)) {
|
||||
$matchedPackage = $package;
|
||||
|
@ -533,6 +554,8 @@ EOT
|
|||
$matches[$index] = $package->getId();
|
||||
}
|
||||
|
||||
$pool = $repositorySet->createPoolForPackage($package->getName());
|
||||
|
||||
// select preferred package according to policy rules
|
||||
if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) {
|
||||
$matchedPackage = $pool->literalToPackage($preferred[0]);
|
||||
|
@ -565,6 +588,9 @@ EOT
|
|||
$this->printLicenses($package);
|
||||
$io->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
|
||||
$io->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
|
||||
if ($installedRepo->hasPackage($package)) {
|
||||
$io->write('<info>path</info> : ' . sprintf('%s', realpath($this->getComposer()->getInstallationManager()->getInstallPath($package))));
|
||||
}
|
||||
$io->write('<info>names</info> : ' . implode(', ', $package->getNames()));
|
||||
|
||||
if ($latestPackage->isAbandoned()) {
|
||||
|
@ -708,40 +734,142 @@ EOT
|
|||
/**
|
||||
* Display the tree
|
||||
*
|
||||
* @param PackageInterface|string $package
|
||||
* @param RepositoryInterface $installedRepo
|
||||
* @param RepositoryInterface $distantRepos
|
||||
* @param array $arrayTree
|
||||
*/
|
||||
protected function displayPackageTree(PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $distantRepos)
|
||||
protected function displayPackageTree(array $arrayTree)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
$io->write(sprintf('<info>%s</info>', $package->getPrettyName()), false);
|
||||
$io->write(' ' . $package->getPrettyVersion(), false);
|
||||
$io->write(' ' . strtok($package->getDescription(), "\r\n"));
|
||||
foreach ($arrayTree as $package) {
|
||||
$io->write(sprintf('<info>%s</info>', $package['name']), false);
|
||||
$io->write(' ' . $package['version'], false);
|
||||
$io->write(' ' . strtok($package['description'], "\r\n"));
|
||||
|
||||
if (is_object($package)) {
|
||||
$requires = $package->getRequires();
|
||||
ksort($requires);
|
||||
if (isset($package['requires'])) {
|
||||
$requires = $package['requires'];
|
||||
$treeBar = '├';
|
||||
$j = 0;
|
||||
$total = count($requires);
|
||||
foreach ($requires as $requireName => $require) {
|
||||
foreach ($requires as $require) {
|
||||
$requireName = $require['name'];
|
||||
$j++;
|
||||
if ($j == 0) {
|
||||
$this->writeTreeLine($treeBar);
|
||||
}
|
||||
if ($j == $total) {
|
||||
if ($j === $total) {
|
||||
$treeBar = '└';
|
||||
}
|
||||
$level = 1;
|
||||
$color = $this->colors[$level];
|
||||
$info = sprintf('%s──<%s>%s</%s> %s', $treeBar, $color, $requireName, $color, $require->getPrettyConstraint());
|
||||
$info = sprintf(
|
||||
'%s──<%s>%s</%s> %s',
|
||||
$treeBar,
|
||||
$color,
|
||||
$requireName,
|
||||
$color,
|
||||
$require['version']
|
||||
);
|
||||
$this->writeTreeLine($info);
|
||||
|
||||
$treeBar = str_replace('└', ' ', $treeBar);
|
||||
$packagesInTree = array($package['name'], $requireName);
|
||||
|
||||
$this->displayTree($require, $packagesInTree, $treeBar, $level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the package tree
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @param RepositoryInterface $installedRepo
|
||||
* @param RepositoryInterface $distantRepos
|
||||
* @return array
|
||||
*/
|
||||
protected function generatePackageTree(
|
||||
PackageInterface $package,
|
||||
RepositoryInterface $installedRepo,
|
||||
RepositoryInterface $distantRepos
|
||||
) {
|
||||
$requires = $package->getRequires();
|
||||
ksort($requires);
|
||||
$children = array();
|
||||
foreach ($requires as $requireName => $require) {
|
||||
$packagesInTree = array($package->getName(), $requireName);
|
||||
|
||||
$this->displayTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree, $treeBar, $level + 1);
|
||||
$treeChildDesc = array(
|
||||
'name' => $requireName,
|
||||
'version' => $require->getPrettyConstraint(),
|
||||
);
|
||||
|
||||
$deepChildren = $this->addTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree);
|
||||
|
||||
if ($deepChildren) {
|
||||
$treeChildDesc['requires'] = $deepChildren;
|
||||
}
|
||||
|
||||
$children[] = $treeChildDesc;
|
||||
}
|
||||
$tree = array(
|
||||
'name' => $package->getPrettyName(),
|
||||
'version' => $package->getPrettyVersion(),
|
||||
'description' => $package->getDescription(),
|
||||
);
|
||||
|
||||
if ($children) {
|
||||
$tree['requires'] = $children;
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a package tree
|
||||
*
|
||||
* @param array|string $package
|
||||
* @param array $packagesInTree
|
||||
* @param string $previousTreeBar
|
||||
* @param int $level
|
||||
*/
|
||||
protected function displayTree(
|
||||
$package,
|
||||
array $packagesInTree,
|
||||
$previousTreeBar = '├',
|
||||
$level = 1
|
||||
) {
|
||||
$previousTreeBar = str_replace('├', '│', $previousTreeBar);
|
||||
if (is_array($package) && isset($package['requires'])) {
|
||||
$requires = $package['requires'];
|
||||
$treeBar = $previousTreeBar . ' ├';
|
||||
$i = 0;
|
||||
$total = count($requires);
|
||||
foreach ($requires as $require) {
|
||||
$currentTree = $packagesInTree;
|
||||
$i++;
|
||||
if ($i === $total) {
|
||||
$treeBar = $previousTreeBar . ' └';
|
||||
}
|
||||
$colorIdent = $level % count($this->colors);
|
||||
$color = $this->colors[$colorIdent];
|
||||
|
||||
$circularWarn = in_array(
|
||||
$require['name'],
|
||||
$currentTree,
|
||||
true
|
||||
) ? '(circular dependency aborted here)' : '';
|
||||
$info = rtrim(sprintf(
|
||||
'%s──<%s>%s</%s> %s %s',
|
||||
$treeBar,
|
||||
$color,
|
||||
$require['name'],
|
||||
$color,
|
||||
$require['version'],
|
||||
$circularWarn
|
||||
));
|
||||
$this->writeTreeLine($info);
|
||||
|
||||
$treeBar = str_replace('└', ' ', $treeBar);
|
||||
|
||||
$currentTree[] = $require['name'];
|
||||
$this->displayTree($require, $currentTree, $treeBar, $level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -754,41 +882,48 @@ EOT
|
|||
* @param RepositoryInterface $installedRepo
|
||||
* @param RepositoryInterface $distantRepos
|
||||
* @param array $packagesInTree
|
||||
* @param string $previousTreeBar
|
||||
* @param int $level
|
||||
* @return array
|
||||
*/
|
||||
protected function displayTree($name, $package, RepositoryInterface $installedRepo, RepositoryInterface $distantRepos, array $packagesInTree, $previousTreeBar = '├', $level = 1)
|
||||
{
|
||||
$previousTreeBar = str_replace('├', '│', $previousTreeBar);
|
||||
list($package, $versions) = $this->getPackage($installedRepo, $distantRepos, $name, $package->getPrettyConstraint() === 'self.version' ? $package->getConstraint() : $package->getPrettyConstraint());
|
||||
protected function addTree(
|
||||
$name,
|
||||
$package,
|
||||
RepositoryInterface $installedRepo,
|
||||
RepositoryInterface $distantRepos,
|
||||
array $packagesInTree
|
||||
) {
|
||||
$children = array();
|
||||
list($package, $versions) = $this->getPackage(
|
||||
$installedRepo,
|
||||
$distantRepos,
|
||||
$name,
|
||||
$package->getPrettyConstraint() === 'self.version' ? $package->getConstraint() : $package->getPrettyConstraint()
|
||||
);
|
||||
if (is_object($package)) {
|
||||
$requires = $package->getRequires();
|
||||
ksort($requires);
|
||||
$treeBar = $previousTreeBar . ' ├';
|
||||
$i = 0;
|
||||
$total = count($requires);
|
||||
foreach ($requires as $requireName => $require) {
|
||||
$currentTree = $packagesInTree;
|
||||
$i++;
|
||||
if ($i == $total) {
|
||||
$treeBar = $previousTreeBar . ' └';
|
||||
}
|
||||
$colorIdent = $level % count($this->colors);
|
||||
$color = $this->colors[$colorIdent];
|
||||
|
||||
$circularWarn = in_array($requireName, $currentTree) ? '(circular dependency aborted here)' : '';
|
||||
$info = rtrim(sprintf('%s──<%s>%s</%s> %s %s', $treeBar, $color, $requireName, $color, $require->getPrettyConstraint(), $circularWarn));
|
||||
$this->writeTreeLine($info);
|
||||
$treeChildDesc = array(
|
||||
'name' => $requireName,
|
||||
'version' => $require->getPrettyConstraint(),
|
||||
);
|
||||
|
||||
$treeBar = str_replace('└', ' ', $treeBar);
|
||||
if (!in_array($requireName, $currentTree)) {
|
||||
if (!in_array($requireName, $currentTree, true)) {
|
||||
$currentTree[] = $requireName;
|
||||
$this->displayTree($requireName, $require, $installedRepo, $distantRepos, $currentTree, $treeBar, $level + 1);
|
||||
$deepChildren = $this->addTree($requireName, $require, $installedRepo, $distantRepos, $currentTree);
|
||||
if ($deepChildren) {
|
||||
$treeChildDesc['requires'] = $deepChildren;
|
||||
}
|
||||
}
|
||||
|
||||
$children[] = $treeChildDesc;
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
private function updateStatusToVersionStyle($updateStatus)
|
||||
{
|
||||
// 'up-to-date' is printed green
|
||||
|
@ -834,13 +969,13 @@ EOT
|
|||
* @param string $phpVersion
|
||||
* @param bool $minorOnly
|
||||
*
|
||||
* @return PackageInterface|null
|
||||
* @return PackageInterface|false
|
||||
*/
|
||||
private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false)
|
||||
{
|
||||
// find the latest version allowed in this pool
|
||||
// find the latest version allowed in this repo set
|
||||
$name = $package->getName();
|
||||
$versionSelector = new VersionSelector($this->getPool($composer));
|
||||
$versionSelector = new VersionSelector($this->getRepositorySet($composer));
|
||||
$stability = $composer->getPackage()->getMinimumStability();
|
||||
$flags = $composer->getPackage()->getStabilityFlags();
|
||||
if (isset($flags[$name])) {
|
||||
|
@ -864,13 +999,13 @@ EOT
|
|||
return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability);
|
||||
}
|
||||
|
||||
private function getPool(Composer $composer)
|
||||
private function getRepositorySet(Composer $composer)
|
||||
{
|
||||
if (!$this->pool) {
|
||||
$this->pool = new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags());
|
||||
$this->pool->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
|
||||
if (!$this->repositorySet) {
|
||||
$this->repositorySet = new RepositorySet(array(), $composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags());
|
||||
$this->repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
|
||||
}
|
||||
|
||||
return $this->pool;
|
||||
return $this->repositorySet;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,23 +36,33 @@ class StatusCommand extends BaseCommand
|
|||
const EXIT_CODE_UNPUSHED_CHANGES = 2;
|
||||
const EXIT_CODE_VERSION_CHANGES = 4;
|
||||
|
||||
/**
|
||||
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('status')
|
||||
->setDescription('Shows a list of locally modified packages.')
|
||||
->setDescription('Shows a list of locally modified packages, for packages installed from source.')
|
||||
->setDefinition(array(
|
||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Show modified files for each directory that contains changes.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The status command displays a list of dependencies that have
|
||||
been modified locally.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#status
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int|null
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// init repos
|
||||
|
@ -80,7 +90,7 @@ EOT
|
|||
|
||||
// list packages
|
||||
foreach ($installedRepo->getCanonicalPackages() as $package) {
|
||||
$downloader = $dm->getDownloaderForInstalledPackage($package);
|
||||
$downloader = $dm->getDownloaderForPackage($package);
|
||||
$targetDir = $im->getInstallPath($package);
|
||||
|
||||
if ($downloader instanceof ChangeReportInterface) {
|
||||
|
|
|
@ -31,17 +31,22 @@ class SuggestsCommand extends BaseCommand
|
|||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
|
||||
The <info>%command.name%</info> command shows a sorted list of suggested packages.
|
||||
|
||||
Enabling <info>-v</info> implies <info>--by-package --by-suggestion</info>, showing both lists.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#suggests
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$lock = $this->getComposer()->getLocker()->getLockData();
|
||||
|
@ -116,7 +121,7 @@ EOT
|
|||
$io->write(sprintf('<info>%s</info>', $suggestion));
|
||||
}
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Grouped by package
|
||||
|
|
|
@ -34,7 +34,7 @@ class UpdateCommand extends BaseCommand
|
|||
{
|
||||
$this
|
||||
->setName('update')
|
||||
->setAliases(array('upgrade'))
|
||||
->setAliases(array('u', 'upgrade'))
|
||||
->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.')
|
||||
->setDefinition(array(
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'),
|
||||
|
@ -61,7 +61,8 @@ class UpdateCommand extends BaseCommand
|
|||
new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
|
||||
new InputOption('root-reqs', null, InputOption::VALUE_NONE, 'Restricts the update to your first degree dependencies.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The <info>update</info> command reads the composer.json file from the
|
||||
current directory, processes it, and updates, removes or installs all the
|
||||
dependencies.
|
||||
|
@ -80,6 +81,7 @@ from a specific vendor:
|
|||
|
||||
To select packages names interactively with auto-completion use <info>-i</info>.
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#update-u
|
||||
EOT
|
||||
)
|
||||
;
|
||||
|
@ -119,8 +121,6 @@ EOT
|
|||
}
|
||||
}
|
||||
|
||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
|
|
|
@ -39,14 +39,15 @@ class ValidateCommand extends BaseCommand
|
|||
->setName('validate')
|
||||
->setDescription('Validates a composer.json and composer.lock.')
|
||||
->setDefinition(array(
|
||||
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
|
||||
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'),
|
||||
new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'),
|
||||
new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
|
||||
new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'),
|
||||
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'),
|
||||
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
->setHelp(
|
||||
<<<EOT
|
||||
The validate command validates a given composer.json and composer.lock
|
||||
|
||||
Exit codes in case of errors are:
|
||||
|
@ -54,6 +55,7 @@ Exit codes in case of errors are:
|
|||
2 validation error(s)
|
||||
3 file unreadable or missing
|
||||
|
||||
Read more at https://getcomposer.org/doc/03-cli.md#validate
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
@ -88,15 +90,16 @@ EOT
|
|||
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
|
||||
|
||||
$lockErrors = array();
|
||||
$composer = Factory::create($io, $file);
|
||||
$composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins'));
|
||||
$locker = $composer->getLocker();
|
||||
if ($locker->isLocked() && !$locker->isFresh()) {
|
||||
$lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update`.';
|
||||
}
|
||||
|
||||
$this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true);
|
||||
$this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true, $isStrict);
|
||||
|
||||
$exitCode = $errors || ($publishErrors && $checkPublish) || ($lockErrors && $checkLock) ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||
// $errors include publish and lock errors when exists
|
||||
$exitCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||
|
||||
if ($input->getOption('with-dependencies')) {
|
||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||
|
@ -107,7 +110,7 @@ EOT
|
|||
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
|
||||
$this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors);
|
||||
|
||||
$depCode = $errors || ($publishErrors && $checkPublish) ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||
$depCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||
$exitCode = max($depCode, $exitCode);
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +123,7 @@ EOT
|
|||
return $exitCode;
|
||||
}
|
||||
|
||||
private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false)
|
||||
private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false, $isStrict = false)
|
||||
{
|
||||
if (!$errors && !$publishErrors && !$warnings) {
|
||||
$io->write('<info>' . $name . ' is valid</info>');
|
||||
|
@ -140,16 +143,18 @@ EOT
|
|||
}
|
||||
|
||||
// If checking publish errors, display them as errors, otherwise just show them as warnings
|
||||
// Skip when it is a strict check and we don't want to check publish errors
|
||||
if ($checkPublish) {
|
||||
$errors = array_merge($errors, $publishErrors);
|
||||
} else {
|
||||
} elseif (!$isStrict) {
|
||||
$warnings = array_merge($warnings, $publishErrors);
|
||||
}
|
||||
|
||||
// If checking lock errors, display them as errors, otherwise just show them as warnings
|
||||
// Skip when it is a strict check and we don't want to check lock errors
|
||||
if ($checkLock) {
|
||||
$errors = array_merge($errors, $lockErrors);
|
||||
} else {
|
||||
} elseif (!$isStrict) {
|
||||
$warnings = array_merge($warnings, $lockErrors);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ class Compiler
|
|||
foreach ($finder as $file) {
|
||||
$this->addFile($phar, $file, false);
|
||||
}
|
||||
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/seld/cli-prompt/res/hiddeninput.exe'), false);
|
||||
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/symfony/console/Resources/bin/hiddeninput.exe'), false);
|
||||
|
||||
$finder = new Finder();
|
||||
$finder->files()
|
||||
|
@ -117,12 +117,13 @@ class Compiler
|
|||
->exclude('docs')
|
||||
->in(__DIR__.'/../../vendor/symfony/')
|
||||
->in(__DIR__.'/../../vendor/seld/jsonlint/')
|
||||
->in(__DIR__.'/../../vendor/seld/cli-prompt/')
|
||||
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
|
||||
->in(__DIR__.'/../../vendor/composer/spdx-licenses/')
|
||||
->in(__DIR__.'/../../vendor/composer/semver/')
|
||||
->in(__DIR__.'/../../vendor/composer/ca-bundle/')
|
||||
->in(__DIR__.'/../../vendor/composer/xdebug-handler/')
|
||||
->in(__DIR__.'/../../vendor/psr/')
|
||||
->in(__DIR__.'/../../vendor/react/')
|
||||
->sort($finderSort)
|
||||
;
|
||||
|
||||
|
@ -193,6 +194,7 @@ class Compiler
|
|||
$content = str_replace('@package_version@', $this->version, $content);
|
||||
$content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content);
|
||||
$content = str_replace('@release_date@', $this->versionDate->format('Y-m-d H:i:s'), $content);
|
||||
$content = preg_replace('{SOURCE_VERSION = \'[^\']+\';}', 'SOURCE_VERSION = \'\';', $content);
|
||||
}
|
||||
|
||||
$phar->addFromString($path, $content);
|
||||
|
@ -255,7 +257,7 @@ class Compiler
|
|||
*/
|
||||
|
||||
// Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264
|
||||
if (extension_loaded('apc') && ini_get('apc.enable_cli') && ini_get('apc.cache_by_default')) {
|
||||
if (extension_loaded('apc') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.cache_by_default'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
if (version_compare(phpversion('apc'), '3.0.12', '>=')) {
|
||||
ini_set('apc.cache_by_default', 0);
|
||||
} else {
|
||||
|
|
|
@ -29,9 +29,46 @@ use Composer\Package\Archiver\ArchiveManager;
|
|||
*/
|
||||
class Composer
|
||||
{
|
||||
/*
|
||||
* Examples of the following constants in the various configurations they can be in
|
||||
*
|
||||
* releases (phar):
|
||||
* const VERSION = '1.8.2';
|
||||
* const BRANCH_ALIAS_VERSION = '';
|
||||
* const RELEASE_DATE = '2019-01-29 15:00:53';
|
||||
* const SOURCE_VERSION = '';
|
||||
*
|
||||
* snapshot builds (phar):
|
||||
* const VERSION = 'd3873a05650e168251067d9648845c220c50e2d7';
|
||||
* const BRANCH_ALIAS_VERSION = '1.9-dev';
|
||||
* const RELEASE_DATE = '2019-02-20 07:43:56';
|
||||
* const SOURCE_VERSION = '';
|
||||
*
|
||||
* source (git clone):
|
||||
* const VERSION = '@package_version@';
|
||||
* const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@';
|
||||
* const RELEASE_DATE = '@release_date@';
|
||||
* const SOURCE_VERSION = '1.8-dev+source';
|
||||
*/
|
||||
const VERSION = '@package_version@';
|
||||
const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@';
|
||||
const RELEASE_DATE = '@release_date@';
|
||||
const SOURCE_VERSION = '2.0-dev+source';
|
||||
|
||||
public static function getVersion()
|
||||
{
|
||||
// no replacement done, this must be a source checkout
|
||||
if (self::VERSION === '@package_version'.'@') {
|
||||
return self::SOURCE_VERSION;
|
||||
}
|
||||
|
||||
// we have a branch alias and version is a commit id, this must be a snapshot build
|
||||
if (self::BRANCH_ALIAS_VERSION !== '' && preg_match('{^[a-f0-9]{40}$}', self::VERSION)) {
|
||||
return self::BRANCH_ALIAS_VERSION.'+'.self::VERSION;
|
||||
}
|
||||
|
||||
return self::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Package\RootPackageInterface
|
||||
|
|
|
@ -16,6 +16,7 @@ use Composer\Config\ConfigSourceInterface;
|
|||
use Composer\Downloader\TransportException;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Platform;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -61,6 +62,7 @@ class Config
|
|||
'archive-format' => 'tar',
|
||||
'archive-dir' => '.',
|
||||
'htaccess-protect' => true,
|
||||
'use-github-api' => true,
|
||||
// valid keys without defaults (auth config stuff):
|
||||
// bitbucket-oauth
|
||||
// github-oauth
|
||||
|
@ -72,7 +74,7 @@ class Config
|
|||
public static $defaultRepositories = array(
|
||||
'packagist.org' => array(
|
||||
'type' => 'composer',
|
||||
'url' => 'https?://packagist.org',
|
||||
'url' => 'https?://repo.packagist.org',
|
||||
'allow_ssl_downgrade' => true,
|
||||
),
|
||||
);
|
||||
|
@ -216,7 +218,6 @@ class Config
|
|||
case 'cache-vcs-dir':
|
||||
case 'cafile':
|
||||
case 'capath':
|
||||
case 'htaccess-protect':
|
||||
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
|
||||
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
|
||||
|
||||
|
@ -230,6 +231,13 @@ class Config
|
|||
|
||||
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
||||
|
||||
case 'htaccess-protect':
|
||||
$value = $this->getComposerEnv('COMPOSER_HTACCESS_PROTECT');
|
||||
if (false === $value) {
|
||||
$value = $this->config[$key];
|
||||
}
|
||||
return $value !== 'false' && (bool) $value;
|
||||
|
||||
case 'cache-ttl':
|
||||
return (int) $this->config[$key];
|
||||
|
||||
|
@ -245,9 +253,11 @@ class Config
|
|||
case 'g':
|
||||
$size *= 1024;
|
||||
// intentional fallthrough
|
||||
// no break
|
||||
case 'm':
|
||||
$size *= 1024;
|
||||
// intentional fallthrough
|
||||
// no break
|
||||
case 'k':
|
||||
$size *= 1024;
|
||||
break;
|
||||
|
@ -315,10 +325,10 @@ class Config
|
|||
|
||||
case 'disable-tls':
|
||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
||||
|
||||
case 'secure-http':
|
||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
||||
|
||||
case 'use-github-api':
|
||||
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
|
||||
default:
|
||||
if (!isset($this->config[$key])) {
|
||||
return null;
|
||||
|
@ -450,4 +460,20 @@ class Config
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by long-running custom scripts in composer.json
|
||||
*
|
||||
* "scripts": {
|
||||
* "watch": [
|
||||
* "Composer\\Config::disableProcessTimeout",
|
||||
* "vendor/bin/long-running-script --watch"
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
public static function disableProcessTimeout()
|
||||
{
|
||||
// Override global timeout set earlier by environment or config
|
||||
ProcessExecutor::setTimeout(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ interface ConfigSourceInterface
|
|||
* Add a config setting
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param string $value Value
|
||||
* @param string|array $value Value
|
||||
*/
|
||||
public function addConfigSetting($name, $value);
|
||||
|
||||
|
|
|
@ -135,10 +135,10 @@ class JsonConfigSource implements ConfigSourceInterface
|
|||
public function addProperty($name, $value)
|
||||
{
|
||||
$this->manipulateJson('addProperty', $name, $value, function (&$config, $key, $val) {
|
||||
if (substr($key, 0, 6) === 'extra.') {
|
||||
if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') {
|
||||
$bits = explode('.', $key);
|
||||
$last = array_pop($bits);
|
||||
$arr = &$config['extra'];
|
||||
$arr = &$config[reset($bits)];
|
||||
foreach ($bits as $bit) {
|
||||
if (!isset($arr[$bit])) {
|
||||
$arr[$bit] = array();
|
||||
|
@ -159,10 +159,10 @@ class JsonConfigSource implements ConfigSourceInterface
|
|||
{
|
||||
$authConfig = $this->authConfig;
|
||||
$this->manipulateJson('removeProperty', $name, function (&$config, $key) {
|
||||
if (substr($key, 0, 6) === 'extra.') {
|
||||
if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') {
|
||||
$bits = explode('.', $key);
|
||||
$last = array_pop($bits);
|
||||
$arr = &$config['extra'];
|
||||
$arr = &$config[reset($bits)];
|
||||
foreach ($bits as $bit) {
|
||||
if (!isset($arr[$bit])) {
|
||||
return;
|
||||
|
@ -193,6 +193,10 @@ class JsonConfigSource implements ConfigSourceInterface
|
|||
{
|
||||
$this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) {
|
||||
unset($config[$type][$name]);
|
||||
|
||||
if (0 === count($config[$type])) {
|
||||
unset($config[$type]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -255,7 +259,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
|||
*
|
||||
* @param array $array
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
* @return int
|
||||
*/
|
||||
private function arrayUnshiftRef(&$array, &$value)
|
||||
{
|
||||
|
|
|
@ -12,9 +12,13 @@
|
|||
|
||||
namespace Composer\Console;
|
||||
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Util\Platform;
|
||||
use Composer\Util\Silencer;
|
||||
use Symfony\Component\Console\Application as BaseApplication;
|
||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -85,7 +89,9 @@ class Application extends BaseApplication
|
|||
});
|
||||
}
|
||||
|
||||
parent::__construct('Composer', Composer::VERSION);
|
||||
$this->io = new NullIO();
|
||||
|
||||
parent::__construct('Composer', Composer::getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,9 +113,16 @@ class Application extends BaseApplication
|
|||
{
|
||||
$this->disablePluginsByDefault = $input->hasParameterOption('--no-plugins');
|
||||
|
||||
$io = $this->io = new ConsoleIO($input, $output, $this->getHelperSet());
|
||||
$io = $this->io = new ConsoleIO($input, $output, new HelperSet(array(
|
||||
new QuestionHelper(),
|
||||
)));
|
||||
ErrorHandler::register($io);
|
||||
|
||||
if ($input->hasParameterOption('--no-cache')) {
|
||||
$io->writeError('Disabling cache usage', true, IOInterface::DEBUG);
|
||||
putenv('COMPOSER_CACHE_DIR='.(Platform::isWindows() ? 'nul' : '/dev/null'));
|
||||
}
|
||||
|
||||
// switch working dir
|
||||
if ($newWorkDir = $this->getNewWorkingDir($input)) {
|
||||
$oldWorkingDir = getcwd();
|
||||
|
@ -122,6 +135,9 @@ class Application extends BaseApplication
|
|||
if ($name = $this->getCommandName($input)) {
|
||||
try {
|
||||
$commandName = $this->find($name)->getName();
|
||||
} catch (CommandNotFoundException $e) {
|
||||
// we'll check command validity again later after plugins are loaded
|
||||
$commandName = false;
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +190,7 @@ class Application extends BaseApplication
|
|||
if (!$isProxyCommand) {
|
||||
$io->writeError(sprintf(
|
||||
'Running %s (%s) with %s on %s',
|
||||
Composer::VERSION,
|
||||
Composer::getVersion(),
|
||||
Composer::RELEASE_DATE,
|
||||
defined('HHVM_VERSION') ? 'HHVM '.HHVM_VERSION : 'PHP '.PHP_VERSION,
|
||||
function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknown OS'
|
||||
|
@ -200,6 +216,12 @@ class Application extends BaseApplication
|
|||
if (function_exists('posix_getuid') && posix_getuid() === 0) {
|
||||
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
||||
$io->writeError('<warning>Do not run Composer as root/super user! See https://getcomposer.org/root for details</warning>');
|
||||
|
||||
if ($io->isInteractive()) {
|
||||
if (!$io->askConfirmation('<info>Continue as root/super user</info> [<comment>yes</comment>]? ', true)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($uid = (int) getenv('SUDO_UID')) {
|
||||
// Silently clobber any sudo credentials on the invoking user to avoid privilege escalations later on
|
||||
|
@ -255,14 +277,14 @@ class Application extends BaseApplication
|
|||
}
|
||||
|
||||
if (isset($startTime)) {
|
||||
$io->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
|
||||
$io->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MiB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MiB), time: '.round(microtime(true) - $startTime, 2).'s');
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
return $result;
|
||||
} catch (ScriptExecutionException $e) {
|
||||
return $e->getCode();
|
||||
return (int) $e->getCode();
|
||||
} catch (\Exception $e) {
|
||||
$this->hintCommonErrors($e);
|
||||
restore_error_handler();
|
||||
|
@ -357,6 +379,9 @@ class Application extends BaseApplication
|
|||
public function resetComposer()
|
||||
{
|
||||
$this->composer = null;
|
||||
if ($this->getIO() && method_exists($this->getIO(), 'resetAuthentications')) {
|
||||
$this->getIO()->resetAuthentications();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -418,7 +443,7 @@ class Application extends BaseApplication
|
|||
*/
|
||||
public function getLongVersion()
|
||||
{
|
||||
if (Composer::BRANCH_ALIAS_VERSION) {
|
||||
if (Composer::BRANCH_ALIAS_VERSION && Composer::BRANCH_ALIAS_VERSION !== '@package_branch_alias_version'.'@') {
|
||||
return sprintf(
|
||||
'<info>%s</info> version <comment>%s (%s)</comment> %s',
|
||||
$this->getName(),
|
||||
|
@ -440,6 +465,7 @@ class Application extends BaseApplication
|
|||
$definition->addOption(new InputOption('--profile', null, InputOption::VALUE_NONE, 'Display timing and memory usage information'));
|
||||
$definition->addOption(new InputOption('--no-plugins', null, InputOption::VALUE_NONE, 'Whether to disable plugins.'));
|
||||
$definition->addOption(new InputOption('--working-dir', '-d', InputOption::VALUE_REQUIRED, 'If specified, use the given directory as working directory.'));
|
||||
$definition->addOption(new InputOption('--no-cache', null, InputOption::VALUE_NONE, 'Prevent use of the cache'));
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
|
|
@ -196,4 +196,16 @@ class Decisions implements \Iterator, \Countable
|
|||
$this->decisionMap[$packageId] = -$level;
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$decisionMap = $this->decisionMap;
|
||||
ksort($decisionMap);
|
||||
$str = '[';
|
||||
foreach ($decisionMap as $packageId => $level) {
|
||||
$str .= $packageId.':'.$level.',';
|
||||
}
|
||||
$str .= ']';
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,11 +57,6 @@ class DefaultPolicy implements PolicyInterface
|
|||
return $packages;
|
||||
}
|
||||
|
||||
public function getPriority(Pool $pool, PackageInterface $package)
|
||||
{
|
||||
return $pool->getPriority($package->getRepository());
|
||||
}
|
||||
|
||||
public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
|
||||
{
|
||||
$packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals);
|
||||
|
@ -168,7 +163,7 @@ class DefaultPolicy implements PolicyInterface
|
|||
return 1;
|
||||
}
|
||||
|
||||
return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1;
|
||||
return ($pool->getPriority($a->id) > $pool->getPriority($b->id)) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,10 +231,10 @@ class DefaultPolicy implements PolicyInterface
|
|||
}
|
||||
|
||||
if (null === $priority) {
|
||||
$priority = $this->getPriority($pool, $package);
|
||||
$priority = $pool->getPriority($package->id);
|
||||
}
|
||||
|
||||
if ($this->getPriority($pool, $package) != $priority) {
|
||||
if ($pool->getPriority($package->id) != $priority) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ class GenericRule extends Rule
|
|||
|
||||
/**
|
||||
* @param array $literals
|
||||
* @param int $reason A RULE_* constant describing the reason for generating this rule
|
||||
* @param Link|PackageInterface $reasonData
|
||||
* @param int|null $reason A RULE_* constant describing the reason for generating this rule
|
||||
* @param Link|PackageInterface|int|null $reasonData
|
||||
* @param array $job The job this rule was created from
|
||||
*/
|
||||
public function __construct(array $literals, $reason, $reasonData, $job = null)
|
||||
|
@ -75,7 +75,7 @@ class GenericRule extends Rule
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$result = ($this->isDisabled()) ? 'disabled(' : '(';
|
||||
$result = $this->isDisabled() ? 'disabled(' : '(';
|
||||
|
||||
foreach ($this->literals as $i => $literal) {
|
||||
if ($i != 0) {
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\DependencyResolver;
|
|||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Semver\Constraint\EmptyConstraint;
|
||||
|
@ -26,7 +27,7 @@ use Composer\Repository\PlatformRepository;
|
|||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* A package pool contains repositories that provide packages.
|
||||
* A package pool contains all packages for dependency resolution
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -40,113 +41,42 @@ class Pool implements \Countable
|
|||
const MATCH_REPLACE = 3;
|
||||
const MATCH_FILTERED = 4;
|
||||
|
||||
protected $repositories = array();
|
||||
protected $providerRepos = array();
|
||||
protected $packages = array();
|
||||
protected $packageByName = array();
|
||||
protected $packageByExactName = array();
|
||||
protected $acceptableStabilities;
|
||||
protected $stabilityFlags;
|
||||
protected $priorities = array();
|
||||
protected $versionParser;
|
||||
protected $providerCache = array();
|
||||
protected $filterRequires;
|
||||
protected $whitelist = null;
|
||||
protected $id = 1;
|
||||
|
||||
public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
|
||||
public function __construct(array $filterRequires = array())
|
||||
{
|
||||
$this->versionParser = new VersionParser;
|
||||
$this->acceptableStabilities = array();
|
||||
foreach (BasePackage::$stabilities as $stability => $value) {
|
||||
if ($value <= BasePackage::$stabilities[$minimumStability]) {
|
||||
$this->acceptableStabilities[$stability] = $value;
|
||||
}
|
||||
}
|
||||
$this->stabilityFlags = $stabilityFlags;
|
||||
$this->filterRequires = $filterRequires;
|
||||
foreach ($filterRequires as $name => $constraint) {
|
||||
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
|
||||
unset($this->filterRequires[$name]);
|
||||
}
|
||||
}
|
||||
$this->versionParser = new VersionParser;
|
||||
}
|
||||
|
||||
public function setWhitelist($whitelist)
|
||||
public function setPackages(array $packages, array $priorities = array())
|
||||
{
|
||||
$this->whitelist = $whitelist;
|
||||
$this->providerCache = array();
|
||||
}
|
||||
$id = 1;
|
||||
|
||||
/**
|
||||
* Adds a repository and its packages to this package pool
|
||||
*
|
||||
* @param RepositoryInterface $repo A package repository
|
||||
* @param array $rootAliases
|
||||
*/
|
||||
public function addRepository(RepositoryInterface $repo, $rootAliases = array())
|
||||
{
|
||||
if ($repo instanceof CompositeRepository) {
|
||||
$repos = $repo->getRepositories();
|
||||
} else {
|
||||
$repos = array($repo);
|
||||
}
|
||||
|
||||
foreach ($repos as $repo) {
|
||||
$this->repositories[] = $repo;
|
||||
|
||||
$exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface;
|
||||
|
||||
if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
|
||||
$this->providerRepos[] = $repo;
|
||||
$repo->setRootAliases($rootAliases);
|
||||
$repo->resetPackageIds();
|
||||
} else {
|
||||
foreach ($repo->getPackages() as $package) {
|
||||
$names = $package->getNames();
|
||||
$stability = $package->getStability();
|
||||
if ($exempt || $this->isPackageAcceptable($names, $stability)) {
|
||||
$package->setId($this->id++);
|
||||
foreach ($packages as $i => $package) {
|
||||
$this->packages[] = $package;
|
||||
$this->priorities[] = isset($priorities[$i]) ? $priorities[$i] : 0;
|
||||
|
||||
$package->id = $id++;
|
||||
$names = $package->getNames();
|
||||
$this->packageByExactName[$package->getName()][$package->id] = $package;
|
||||
|
||||
foreach ($names as $provided) {
|
||||
$this->packageByName[$provided][] = $package;
|
||||
}
|
||||
|
||||
// handle root package aliases
|
||||
$name = $package->getName();
|
||||
if (isset($rootAliases[$name][$package->getVersion()])) {
|
||||
$alias = $rootAliases[$name][$package->getVersion()];
|
||||
if ($package instanceof AliasPackage) {
|
||||
$package = $package->getAliasOf();
|
||||
}
|
||||
$aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
|
||||
$aliasPackage->setRootPackageAlias(true);
|
||||
$aliasPackage->setId($this->id++);
|
||||
|
||||
$package->getRepository()->addPackage($aliasPackage);
|
||||
$this->packages[] = $aliasPackage;
|
||||
$this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage;
|
||||
|
||||
foreach ($aliasPackage->getNames() as $name) {
|
||||
$this->packageByName[$name][] = $aliasPackage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPriority(RepositoryInterface $repo)
|
||||
public function getPriority($id)
|
||||
{
|
||||
$priority = array_search($repo, $this->repositories, true);
|
||||
|
||||
if (false === $priority) {
|
||||
throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool.");
|
||||
}
|
||||
|
||||
return -$priority;
|
||||
return $this->priorities[$id - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,45 +130,18 @@ class Pool implements \Countable
|
|||
{
|
||||
$candidates = array();
|
||||
|
||||
foreach ($this->providerRepos as $repo) {
|
||||
foreach ($repo->whatProvides($this, $name, $bypassFilters) as $candidate) {
|
||||
$candidates[] = $candidate;
|
||||
if ($candidate->id < 1) {
|
||||
$candidate->setId($this->id++);
|
||||
$this->packages[$this->id - 2] = $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($mustMatchName) {
|
||||
$candidates = array_filter($candidates, function ($candidate) use ($name) {
|
||||
return $candidate->getName() == $name;
|
||||
});
|
||||
if (isset($this->packageByExactName[$name])) {
|
||||
$candidates = array_merge($candidates, $this->packageByExactName[$name]);
|
||||
$candidates = $this->packageByExactName[$name];
|
||||
}
|
||||
} elseif (isset($this->packageByName[$name])) {
|
||||
$candidates = array_merge($candidates, $this->packageByName[$name]);
|
||||
$candidates = $this->packageByName[$name];
|
||||
}
|
||||
|
||||
$matches = $provideMatches = array();
|
||||
$nameMatch = false;
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$aliasOfCandidate = null;
|
||||
|
||||
// alias packages are not white listed, make sure that the package
|
||||
// being aliased is white listed
|
||||
if ($candidate instanceof AliasPackage) {
|
||||
$aliasOfCandidate = $candidate->getAliasOf();
|
||||
}
|
||||
|
||||
if ($this->whitelist !== null && !$bypassFilters && (
|
||||
(!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->id])) ||
|
||||
($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->id]))
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
switch ($this->match($candidate, $name, $constraint, $bypassFilters)) {
|
||||
case self::MATCH_NONE:
|
||||
break;
|
||||
|
@ -296,33 +199,16 @@ class Pool implements \Countable
|
|||
return $prefix.' '.$package->getPrettyString();
|
||||
}
|
||||
|
||||
public function isPackageAcceptable($name, $stability)
|
||||
{
|
||||
foreach ((array) $name as $n) {
|
||||
// allow if package matches the global stability requirement and has no exception
|
||||
if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow if package matches the package-specific stability flag
|
||||
if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the package matches the given constraint directly or through
|
||||
* provided or replaced packages
|
||||
*
|
||||
* @param array|PackageInterface $candidate
|
||||
* @param PackageInterface $candidate
|
||||
* @param string $name Name of the package to be matched
|
||||
* @param ConstraintInterface $constraint The constraint to verify
|
||||
* @return int One of the MATCH* constants of this class or 0 if there is no match
|
||||
*/
|
||||
private function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters)
|
||||
public function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters)
|
||||
{
|
||||
$candidateName = $candidate->getName();
|
||||
$candidateVersion = $candidate->getVersion();
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\DependencyResolver;
|
||||
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\AsyncRepositoryInterface;
|
||||
use Composer\Repository\ComposerRepository;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Repository\LockArrayRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Semver\Constraint\MultiConstraint;
|
||||
|
||||
/**
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class PoolBuilder
|
||||
{
|
||||
private $isPackageAcceptableCallable;
|
||||
private $filterRequires;
|
||||
private $rootAliases;
|
||||
|
||||
private $aliasMap = array();
|
||||
private $nameConstraints = array();
|
||||
|
||||
private $loadedNames = array();
|
||||
|
||||
private $packages = array();
|
||||
private $priorities = array();
|
||||
|
||||
public function __construct($isPackageAcceptableCallable, array $filterRequires = array())
|
||||
{
|
||||
$this->isPackageAcceptableCallable = $isPackageAcceptableCallable;
|
||||
$this->filterRequires = $filterRequires;
|
||||
}
|
||||
|
||||
public function buildPool(array $repositories, array $rootAliases, Request $request)
|
||||
{
|
||||
$pool = new Pool($this->filterRequires);
|
||||
$this->rootAliases = $rootAliases;
|
||||
|
||||
// TODO do we really want the request here? kind of want a root requirements thingy instead
|
||||
$loadNames = array();
|
||||
foreach ($request->getJobs() as $job) {
|
||||
switch ($job['cmd']) {
|
||||
case 'install':
|
||||
$loadNames[$job['packageName']] = $job['constraint'];
|
||||
$this->nameConstraints[$job['packageName']] = $job['constraint'] ? new MultiConstraint(array($job['constraint']), false) : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!empty($loadNames)) {
|
||||
$loadIds = array();
|
||||
foreach ($repositories as $key => $repository) {
|
||||
if ($repository instanceof AsyncRepositoryInterface) {
|
||||
$loadIds[$key] = $repository->requestPackages($loadNames);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($loadNames as $name => $void) {
|
||||
$this->loadedNames[$name] = true;
|
||||
}
|
||||
|
||||
$newLoadNames = array();
|
||||
foreach ($repositories as $key => $repository) {
|
||||
if ($repository instanceof PlatformRepository || $repository instanceof InstalledRepositoryInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($repository instanceof AsyncRepositoryInterface) {
|
||||
// TODO ispackageacceptablecallable in here?
|
||||
$packages = $repository->returnPackages($loadIds[$key]);
|
||||
} else {
|
||||
// TODO should we really pass the callable into here?
|
||||
$packages = $repository->loadPackages($loadNames, $this->isPackageAcceptableCallable);
|
||||
}
|
||||
|
||||
foreach ($packages as $package) {
|
||||
if (call_user_func($this->isPackageAcceptableCallable, $package->getNames(), $package->getStability())) {
|
||||
$newLoadNames += $this->loadPackage($package, $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$loadNames = $newLoadNames;
|
||||
}
|
||||
|
||||
foreach ($this->packages as $i => $package) {
|
||||
// we check all alias related packages at once, so no need ot check individual aliases
|
||||
// isset also checks non-null value
|
||||
if (!$package instanceof AliasPackage && isset($this->nameConstraints[$package->getName()])) {
|
||||
$constraint = $this->nameConstraints[$package->getName()];
|
||||
|
||||
$aliasedPackages = array($i => $package);
|
||||
if (isset($this->aliasMap[spl_object_hash($package)])) {
|
||||
$aliasedPackages += $this->aliasMap[spl_object_hash($package)];
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($aliasedPackages as $packageOrAlias) {
|
||||
if ($constraint->matches(new Constraint('==', $packageOrAlias->getVersion()))) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
foreach ($aliasedPackages as $index => $packageOrAlias) {
|
||||
unset($this->packages[$index]);
|
||||
unset($this->priorities[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($repositories as $key => $repository) {
|
||||
if ($repository instanceof PlatformRepository ||
|
||||
$repository instanceof InstalledRepositoryInterface) {
|
||||
foreach ($repository->getPackages() as $package) {
|
||||
$this->loadPackage($package, $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pool->setPackages($this->packages, $this->priorities);
|
||||
|
||||
unset($this->aliasMap);
|
||||
unset($this->loadedNames);
|
||||
unset($this->nameConstraints);
|
||||
|
||||
return $pool;
|
||||
}
|
||||
|
||||
private function loadPackage(PackageInterface $package, $repoIndex)
|
||||
{
|
||||
$index = count($this->packages);
|
||||
$this->packages[] = $package;
|
||||
$this->priorities[] = -$repoIndex;
|
||||
|
||||
if ($package instanceof AliasPackage) {
|
||||
$this->aliasMap[spl_object_hash($package->getAliasOf())][$index] = $package;
|
||||
}
|
||||
|
||||
// handle root package aliases
|
||||
$name = $package->getName();
|
||||
if (isset($this->rootAliases[$name][$package->getVersion()])) {
|
||||
$alias = $this->rootAliases[$name][$package->getVersion()];
|
||||
if ($package instanceof AliasPackage) {
|
||||
$basePackage = $package->getAliasOf();
|
||||
} else {
|
||||
$basePackage = $package;
|
||||
}
|
||||
$aliasPackage = new AliasPackage($basePackage, $alias['alias_normalized'], $alias['alias']);
|
||||
$aliasPackage->setRootPackageAlias(true);
|
||||
|
||||
$package->getRepository()->addPackage($aliasPackage); // TODO do we need this?
|
||||
$this->packages[] = $aliasPackage;
|
||||
$this->priorities[] = -$repoIndex;
|
||||
$this->aliasMap[spl_object_hash($aliasPackage->getAliasOf())][$index+1] = $aliasPackage;
|
||||
}
|
||||
|
||||
$loadNames = array();
|
||||
foreach ($package->getRequires() as $link) {
|
||||
$require = $link->getTarget();
|
||||
if (!isset($this->loadedNames[$require])) {
|
||||
$loadNames[$require] = null;
|
||||
}
|
||||
if ($linkConstraint = $link->getConstraint()) {
|
||||
if (!array_key_exists($require, $this->nameConstraints)) {
|
||||
$this->nameConstraints[$require] = new MultiConstraint(array($linkConstraint), false);
|
||||
} elseif ($this->nameConstraints[$require]) {
|
||||
// TODO addConstraint function?
|
||||
$this->nameConstraints[$require] = new MultiConstraint(array_merge(array($linkConstraint), $this->nameConstraints[$require]->getConstraints()), false);
|
||||
}
|
||||
} else {
|
||||
$this->nameConstraints[$require] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $loadNames;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace Composer\DependencyResolver;
|
||||
|
||||
use Composer\Package\CompletePackageInterface;
|
||||
|
||||
/**
|
||||
* Represents a problem detected while solving dependencies
|
||||
*
|
||||
|
@ -69,7 +71,7 @@ class Problem
|
|||
* @param array $installedMap A map of all installed packages
|
||||
* @return string
|
||||
*/
|
||||
public function getPrettyString(array $installedMap = array())
|
||||
public function getPrettyString(array $installedMap = array(), array $learnedPool = array())
|
||||
{
|
||||
$reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
|
||||
|
||||
|
@ -77,11 +79,13 @@ class Problem
|
|||
reset($reasons);
|
||||
$reason = current($reasons);
|
||||
|
||||
$rule = $reason['rule'];
|
||||
$job = $reason['job'];
|
||||
|
||||
if (isset($job['constraint'])) {
|
||||
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
|
||||
$packageName = $job['packageName'];
|
||||
$constraint = $job['constraint'];
|
||||
|
||||
if (isset($constraint)) {
|
||||
$packages = $this->pool->whatProvides($packageName, $constraint);
|
||||
} else {
|
||||
$packages = array();
|
||||
}
|
||||
|
@ -89,17 +93,26 @@ class Problem
|
|||
if ($job && $job['cmd'] === 'install' && empty($packages)) {
|
||||
|
||||
// handle php/hhvm
|
||||
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
|
||||
$available = $this->pool->whatProvides($job['packageName']);
|
||||
$version = count($available) ? $available[0]->getPrettyVersion() : phpversion();
|
||||
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
|
||||
$version = phpversion();
|
||||
$available = $this->pool->whatProvides($packageName);
|
||||
|
||||
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
|
||||
if (count($available)) {
|
||||
$firstAvailable = reset($available);
|
||||
$version = $firstAvailable->getPrettyVersion();
|
||||
$extra = $firstAvailable->getExtra();
|
||||
if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) {
|
||||
$version .= '; ' . $firstAvailable->getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$msg = "\n - This package requires ".$packageName.$this->constraintToText($constraint).' but ';
|
||||
|
||||
if (defined('HHVM_VERSION') || (count($available) && $packageName === 'hhvm')) {
|
||||
return $msg . 'your HHVM version does not satisfy that requirement.';
|
||||
}
|
||||
|
||||
if ($job['packageName'] === 'hhvm') {
|
||||
if ($packageName === 'hhvm') {
|
||||
return $msg . 'you are running this with PHP and not HHVM.';
|
||||
}
|
||||
|
||||
|
@ -107,39 +120,43 @@ class Problem
|
|||
}
|
||||
|
||||
// handle php extensions
|
||||
if (0 === stripos($job['packageName'], 'ext-')) {
|
||||
$ext = substr($job['packageName'], 4);
|
||||
if (0 === stripos($packageName, 'ext-')) {
|
||||
if (false !== strpos($packageName, ' ')) {
|
||||
return "\n - The requested PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.';
|
||||
}
|
||||
|
||||
$ext = substr($packageName, 4);
|
||||
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
|
||||
|
||||
return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
|
||||
return "\n - The requested PHP extension ".$packageName.$this->constraintToText($constraint).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
|
||||
}
|
||||
|
||||
// handle linked libs
|
||||
if (0 === stripos($job['packageName'], 'lib-')) {
|
||||
if (strtolower($job['packageName']) === 'lib-icu') {
|
||||
if (0 === stripos($packageName, 'lib-')) {
|
||||
if (strtolower($packageName) === 'lib-icu') {
|
||||
$error = extension_loaded('intl') ? 'has the wrong version installed, try upgrading the intl extension.' : 'is missing from your system, make sure the intl extension is loaded.';
|
||||
|
||||
return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error;
|
||||
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' '.$error;
|
||||
}
|
||||
|
||||
return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
|
||||
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
|
||||
}
|
||||
|
||||
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $job['packageName'])) {
|
||||
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $job['packageName']);
|
||||
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
|
||||
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName);
|
||||
|
||||
return "\n - The requested package ".$job['packageName'].' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
|
||||
return "\n - The requested package ".$packageName.' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
|
||||
}
|
||||
|
||||
if ($providers = $this->pool->whatProvides($job['packageName'], $job['constraint'], true, true)) {
|
||||
return "\n - The requested package ".$job['packageName'].$this->constraintToText($job['constraint']).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.';
|
||||
if ($providers = $this->pool->whatProvides($packageName, $constraint, true, true)) {
|
||||
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.';
|
||||
}
|
||||
|
||||
if ($providers = $this->pool->whatProvides($job['packageName'], null, true, true)) {
|
||||
return "\n - The requested package ".$job['packageName'].$this->constraintToText($job['constraint']).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.';
|
||||
if ($providers = $this->pool->whatProvides($packageName, null, true, true)) {
|
||||
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.';
|
||||
}
|
||||
|
||||
return "\n - The requested package ".$job['packageName'].' could not be found in any version, there may be a typo in the package name.';
|
||||
return "\n - The requested package ".$packageName.' could not be found in any version, there may be a typo in the package name.';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +170,7 @@ class Problem
|
|||
$messages[] = $this->jobToText($job);
|
||||
} elseif ($rule) {
|
||||
if ($rule instanceof Rule) {
|
||||
$messages[] = $rule->getPrettyString($this->pool, $installedMap);
|
||||
$messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +182,7 @@ class Problem
|
|||
* Store a reason descriptor but ignore duplicates
|
||||
*
|
||||
* @param string $id A canonical identifier for the reason
|
||||
* @param string $reason The reason descriptor
|
||||
* @param string|array $reason The reason descriptor
|
||||
*/
|
||||
protected function addReason($id, $reason)
|
||||
{
|
||||
|
@ -188,27 +205,29 @@ class Problem
|
|||
*/
|
||||
protected function jobToText($job)
|
||||
{
|
||||
$packageName = $job['packageName'];
|
||||
$constraint = $job['constraint'];
|
||||
switch ($job['cmd']) {
|
||||
case 'install':
|
||||
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
|
||||
$packages = $this->pool->whatProvides($packageName, $constraint);
|
||||
if (!$packages) {
|
||||
return 'No package found to satisfy install request for '.$job['packageName'].$this->constraintToText($job['constraint']);
|
||||
return 'No package found to satisfy install request for '.$packageName.$this->constraintToText($constraint);
|
||||
}
|
||||
|
||||
return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($packages).'.';
|
||||
return 'Installation request for '.$packageName.$this->constraintToText($constraint).' -> satisfiable by '.$this->getPackageList($packages).'.';
|
||||
case 'update':
|
||||
return 'Update request for '.$job['packageName'].$this->constraintToText($job['constraint']).'.';
|
||||
return 'Update request for '.$packageName.$this->constraintToText($constraint).'.';
|
||||
case 'remove':
|
||||
return 'Removal request for '.$job['packageName'].$this->constraintToText($job['constraint']).'';
|
||||
return 'Removal request for '.$packageName.$this->constraintToText($constraint).'';
|
||||
}
|
||||
|
||||
if (isset($job['constraint'])) {
|
||||
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
|
||||
if (isset($constraint)) {
|
||||
$packages = $this->pool->whatProvides($packageName, $constraint);
|
||||
} else {
|
||||
$packages = array();
|
||||
}
|
||||
|
||||
return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->getPackageList($packages).'])';
|
||||
return 'Job(cmd='.$job['cmd'].', target='.$packageName.', packages=['.$this->getPackageList($packages).'])';
|
||||
}
|
||||
|
||||
protected function getPackageList($packages)
|
||||
|
@ -233,6 +252,6 @@ class Problem
|
|||
*/
|
||||
protected function constraintToText($constraint)
|
||||
{
|
||||
return ($constraint) ? ' '.$constraint->getPrettyString() : '';
|
||||
return $constraint ? ' '.$constraint->getPrettyString() : '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ abstract class Rule
|
|||
const BITFIELD_DISABLED = 16;
|
||||
|
||||
protected $bitfield;
|
||||
protected $job;
|
||||
protected $reasonData;
|
||||
|
||||
/**
|
||||
|
@ -67,7 +68,7 @@ abstract class Rule
|
|||
|
||||
public function getJob()
|
||||
{
|
||||
return isset($this->job) ? $this->job : null;
|
||||
return $this->job;
|
||||
}
|
||||
|
||||
abstract public function equals(Rule $rule);
|
||||
|
@ -110,7 +111,7 @@ abstract class Rule
|
|||
|
||||
public function enable()
|
||||
{
|
||||
$this->bitfield = $this->bitfield & ~(255 << self::BITFIELD_DISABLED);
|
||||
$this->bitfield &= ~(255 << self::BITFIELD_DISABLED);
|
||||
}
|
||||
|
||||
public function isDisabled()
|
||||
|
@ -125,7 +126,7 @@ abstract class Rule
|
|||
|
||||
abstract public function isAssertion();
|
||||
|
||||
public function getPrettyString(Pool $pool, array $installedMap = array())
|
||||
public function getPrettyString(Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
||||
{
|
||||
$literals = $this->getLiterals();
|
||||
|
||||
|
@ -174,13 +175,18 @@ abstract class Rule
|
|||
return $text . ' -> your HHVM version does not satisfy that requirement.';
|
||||
}
|
||||
|
||||
if ($targetName === 'hhvm') {
|
||||
return $text . ' -> you are running this with PHP and not HHVM.';
|
||||
}
|
||||
|
||||
$packages = $pool->whatProvides($targetName);
|
||||
$package = count($packages) ? current($packages) : phpversion();
|
||||
|
||||
if ($targetName === 'hhvm') {
|
||||
if ($package instanceof CompletePackage) {
|
||||
return $text . ' -> your HHVM version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
|
||||
} else {
|
||||
return $text . ' -> you are running this with PHP and not HHVM.';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!($package instanceof CompletePackage)) {
|
||||
return $text . ' -> your PHP version ('.phpversion().') does not satisfy that requirement.';
|
||||
}
|
||||
|
@ -229,7 +235,18 @@ abstract class Rule
|
|||
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
|
||||
return $ruleText;
|
||||
case self::RULE_LEARNED:
|
||||
return 'Conclusion: '.$ruleText;
|
||||
// TODO not sure this is a good idea, most of these rules should be listed in the problem anyway
|
||||
$learnedString = '(learned rule, ';
|
||||
if (isset($learnedPool[$this->reasonData])) {
|
||||
foreach ($learnedPool[$this->reasonData] as $learnedRule) {
|
||||
$learnedString .= $learnedRule->getPrettyString($pool, $installedMap, $learnedPool);
|
||||
}
|
||||
} else {
|
||||
$learnedString .= 'reasoning unavailable';
|
||||
}
|
||||
$learnedString .= ')';
|
||||
|
||||
return 'Conclusion: '.$ruleText.' '.$learnedString;
|
||||
case self::RULE_PACKAGE_ALIAS:
|
||||
return $ruleText;
|
||||
default:
|
||||
|
|
|
@ -50,9 +50,7 @@ class Rule2Literals extends Rule
|
|||
|
||||
public function getHash()
|
||||
{
|
||||
$data = unpack('ihash', md5($this->literal1.','.$this->literal2, true));
|
||||
|
||||
return $data['hash'];
|
||||
return $this->literal1.','.$this->literal2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,6 +63,19 @@ class Rule2Literals extends Rule
|
|||
*/
|
||||
public function equals(Rule $rule)
|
||||
{
|
||||
// specialized fast-case
|
||||
if ($rule instanceof self) {
|
||||
if ($this->literal1 !== $rule->literal1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->literal2 !== $rule->literal2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$literals = $rule->getLiterals();
|
||||
if (2 != count($literals)) {
|
||||
return false;
|
||||
|
@ -93,7 +104,7 @@ class Rule2Literals extends Rule
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$result = ($this->isDisabled()) ? 'disabled(' : '(';
|
||||
$result = $this->isDisabled() ? 'disabled(' : '(';
|
||||
|
||||
$result .= $this->literal1 . '|' . $this->literal2 . ')';
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ class RuleSetGenerator
|
|||
protected $rules;
|
||||
protected $jobs;
|
||||
protected $installedMap;
|
||||
protected $whitelistedMap;
|
||||
protected $addedMap;
|
||||
protected $conflictAddedMap;
|
||||
protected $addedPackages;
|
||||
protected $addedPackagesByNames;
|
||||
|
||||
public function __construct(PolicyInterface $policy, Pool $pool)
|
||||
{
|
||||
|
@ -47,7 +49,7 @@ class RuleSetGenerator
|
|||
* reason for generating this rule
|
||||
* @param mixed $reasonData Any data, e.g. the requirement name,
|
||||
* that goes with the reason
|
||||
* @return Rule The generated rule or null if tautological
|
||||
* @return Rule|null The generated rule or null if tautological
|
||||
*/
|
||||
protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
|
||||
{
|
||||
|
@ -114,7 +116,7 @@ class RuleSetGenerator
|
|||
* reason for generating this rule
|
||||
* @param mixed $reasonData Any data, e.g. the package name, that
|
||||
* goes with the reason
|
||||
* @return Rule The generated rule
|
||||
* @return Rule|null The generated rule
|
||||
*/
|
||||
protected function createRule2Literals(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
|
||||
{
|
||||
|
@ -144,47 +146,13 @@ class RuleSetGenerator
|
|||
$this->rules->add($newRule, $type);
|
||||
}
|
||||
|
||||
protected function whitelistFromPackage(PackageInterface $package)
|
||||
{
|
||||
$workQueue = new \SplQueue;
|
||||
$workQueue->enqueue($package);
|
||||
|
||||
while (!$workQueue->isEmpty()) {
|
||||
$package = $workQueue->dequeue();
|
||||
if (isset($this->whitelistedMap[$package->id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->whitelistedMap[$package->id] = true;
|
||||
|
||||
foreach ($package->getRequires() as $link) {
|
||||
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint(), true);
|
||||
|
||||
foreach ($possibleRequires as $require) {
|
||||
$workQueue->enqueue($require);
|
||||
}
|
||||
}
|
||||
|
||||
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null, true);
|
||||
|
||||
foreach ($obsoleteProviders as $provider) {
|
||||
if ($provider === $package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
|
||||
$workQueue->enqueue($provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addRulesForPackage(PackageInterface $package, $ignorePlatformReqs)
|
||||
{
|
||||
$workQueue = new \SplQueue;
|
||||
$workQueue->enqueue($package);
|
||||
|
||||
while (!$workQueue->isEmpty()) {
|
||||
/** @var PackageInterface $package */
|
||||
$package = $workQueue->dequeue();
|
||||
if (isset($this->addedMap[$package->id])) {
|
||||
continue;
|
||||
|
@ -192,6 +160,11 @@ class RuleSetGenerator
|
|||
|
||||
$this->addedMap[$package->id] = true;
|
||||
|
||||
$this->addedPackages[] = $package;
|
||||
foreach ($package->getNames() as $name) {
|
||||
$this->addedPackagesByNames[$name][] = $package;
|
||||
}
|
||||
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
|
||||
continue;
|
||||
|
@ -199,40 +172,15 @@ class RuleSetGenerator
|
|||
|
||||
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link));
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link));
|
||||
|
||||
foreach ($possibleRequires as $require) {
|
||||
$workQueue->enqueue($require);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($package->getConflicts() as $link) {
|
||||
$possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
foreach ($possibleConflicts as $conflict) {
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, $link));
|
||||
}
|
||||
}
|
||||
|
||||
// check obsoletes and implicit obsoletes of a package
|
||||
$isInstalled = (isset($this->installedMap[$package->id]));
|
||||
|
||||
foreach ($package->getReplaces() as $link) {
|
||||
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
foreach ($obsoleteProviders as $provider) {
|
||||
if ($provider === $package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
|
||||
$packageName = $package->getName();
|
||||
$obsoleteProviders = $this->pool->whatProvides($packageName, null);
|
||||
|
||||
foreach ($obsoleteProviders as $provider) {
|
||||
if ($provider === $package) {
|
||||
|
@ -240,10 +188,57 @@ class RuleSetGenerator
|
|||
}
|
||||
|
||||
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
|
||||
} elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRule2Literals($package, $provider, $reason, $package));
|
||||
$reason = ($packageName == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $package));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addConflictRules($ignorePlatformReqs = false)
|
||||
{
|
||||
/** @var PackageInterface $package */
|
||||
foreach ($this->addedPackages as $package) {
|
||||
foreach ($package->getConflicts() as $link) {
|
||||
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PackageInterface $possibleConflict */
|
||||
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
|
||||
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
|
||||
|
||||
if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) {
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// check obsoletes and implicit obsoletes of a package
|
||||
$isInstalled = isset($this->installedMap[$package->id]);
|
||||
|
||||
foreach ($package->getReplaces() as $link) {
|
||||
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PackageInterface $possibleConflict */
|
||||
foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) {
|
||||
if ($provider === $package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||
$reason = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,20 +258,6 @@ class RuleSetGenerator
|
|||
return $impossible;
|
||||
}
|
||||
|
||||
protected function whitelistFromJobs()
|
||||
{
|
||||
foreach ($this->jobs as $job) {
|
||||
switch ($job['cmd']) {
|
||||
case 'install':
|
||||
$packages = $this->pool->whatProvides($job['packageName'], $job['constraint'], true);
|
||||
foreach ($packages as $package) {
|
||||
$this->whitelistFromPackage($package);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addRulesForJobs($ignorePlatformReqs)
|
||||
{
|
||||
foreach ($this->jobs as $job) {
|
||||
|
@ -317,21 +298,21 @@ class RuleSetGenerator
|
|||
$this->rules = new RuleSet;
|
||||
$this->installedMap = $installedMap;
|
||||
|
||||
$this->whitelistedMap = array();
|
||||
foreach ($this->installedMap as $package) {
|
||||
$this->whitelistFromPackage($package);
|
||||
}
|
||||
$this->whitelistFromJobs();
|
||||
|
||||
$this->pool->setWhitelist($this->whitelistedMap);
|
||||
|
||||
$this->addedMap = array();
|
||||
$this->conflictAddedMap = array();
|
||||
$this->addedPackages = array();
|
||||
$this->addedPackagesByNames = array();
|
||||
foreach ($this->installedMap as $package) {
|
||||
$this->addRulesForPackage($package, $ignorePlatformReqs);
|
||||
}
|
||||
|
||||
$this->addRulesForJobs($ignorePlatformReqs);
|
||||
|
||||
$this->addConflictRules($ignorePlatformReqs);
|
||||
|
||||
// Remove references to packages
|
||||
$this->addedPackages = $this->addedPackagesByNames = null;
|
||||
|
||||
return $this->rules;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,9 +127,9 @@ class RuleWatchGraph
|
|||
*
|
||||
* The rule node's watched literals are updated accordingly.
|
||||
*
|
||||
* @param $fromLiteral mixed A literal the node used to watch
|
||||
* @param $toLiteral mixed A literal the node should watch now
|
||||
* @param $node mixed The rule node to be moved
|
||||
* @param int $fromLiteral A literal the node used to watch
|
||||
* @param int $toLiteral A literal the node should watch now
|
||||
* @param RuleWatchNode $node The rule node to be moved
|
||||
*/
|
||||
protected function moveWatch($fromLiteral, $toLiteral, $node)
|
||||
{
|
||||
|
|
|
@ -37,8 +37,9 @@ class RuleWatchNode
|
|||
|
||||
$literals = $rule->getLiterals();
|
||||
|
||||
$this->watch1 = count($literals) > 0 ? $literals[0] : 0;
|
||||
$this->watch2 = count($literals) > 1 ? $literals[1] : 0;
|
||||
$literalCount = count($literals);
|
||||
$this->watch1 = $literalCount > 0 ? $literals[0] : 0;
|
||||
$this->watch2 = $literalCount > 1 ? $literals[1] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
namespace Composer\DependencyResolver;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositorySet;
|
||||
|
||||
/**
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
|
@ -27,10 +29,10 @@ class Solver
|
|||
/** @var PolicyInterface */
|
||||
protected $policy;
|
||||
/** @var Pool */
|
||||
protected $pool;
|
||||
protected $pool = null;
|
||||
/** @var RepositoryInterface */
|
||||
protected $installed;
|
||||
/** @var Ruleset */
|
||||
/** @var RuleSet */
|
||||
protected $rules;
|
||||
/** @var RuleSetGenerator */
|
||||
protected $ruleSetGenerator;
|
||||
|
@ -43,7 +45,7 @@ class Solver
|
|||
protected $watchGraph;
|
||||
/** @var Decisions */
|
||||
protected $decisions;
|
||||
/** @var int[] */
|
||||
/** @var PackageInterface[] */
|
||||
protected $installedMap;
|
||||
|
||||
/** @var int */
|
||||
|
@ -57,6 +59,9 @@ class Solver
|
|||
/** @var array */
|
||||
protected $learnedWhy = array();
|
||||
|
||||
/** @var bool */
|
||||
public $testFlagLearnedPositiveLiteral = false;
|
||||
|
||||
/** @var IOInterface */
|
||||
protected $io;
|
||||
|
||||
|
@ -72,7 +77,6 @@ class Solver
|
|||
$this->policy = $policy;
|
||||
$this->pool = $pool;
|
||||
$this->installed = $installed;
|
||||
$this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,6 +87,11 @@ class Solver
|
|||
return count($this->rules);
|
||||
}
|
||||
|
||||
public function getPool()
|
||||
{
|
||||
return $this->pool;
|
||||
}
|
||||
|
||||
// aka solver_makeruledecisions
|
||||
|
||||
private function makeAssertionRuleDecisions()
|
||||
|
@ -100,7 +109,7 @@ class Solver
|
|||
$literals = $rule->getLiterals();
|
||||
$literal = $literals[0];
|
||||
|
||||
if (!$this->decisions->decided(abs($literal))) {
|
||||
if (!$this->decisions->decided($literal)) {
|
||||
$this->decisions->decide($literal, 1, $rule);
|
||||
continue;
|
||||
}
|
||||
|
@ -211,6 +220,9 @@ class Solver
|
|||
$this->jobs = $request->getJobs();
|
||||
|
||||
$this->setupInstalledMap();
|
||||
|
||||
$this->io->writeError('Generating rules', true, IOInterface::DEBUG);
|
||||
$this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool);
|
||||
$this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs);
|
||||
$this->checkForRootRequireProblems($ignorePlatformReqs);
|
||||
$this->decisions = new Decisions($this->pool);
|
||||
|
@ -226,6 +238,7 @@ class Solver
|
|||
$this->io->writeError('Resolving dependencies through SAT', true, IOInterface::DEBUG);
|
||||
$before = microtime(true);
|
||||
$this->runSat(true);
|
||||
$this->io->writeError('', true, IOInterface::DEBUG);
|
||||
$this->io->writeError(sprintf('Dependency resolution completed in %.3f seconds', microtime(true) - $before), true, IOInterface::VERBOSE);
|
||||
|
||||
// decide to remove everything that's installed and undecided
|
||||
|
@ -236,7 +249,7 @@ class Solver
|
|||
}
|
||||
|
||||
if ($this->problems) {
|
||||
throw new SolverProblemsException($this->problems, $this->installedMap);
|
||||
throw new SolverProblemsException($this->problems, $this->installedMap, $this->learnedPool);
|
||||
}
|
||||
|
||||
$transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisions);
|
||||
|
@ -469,7 +482,10 @@ class Solver
|
|||
unset($seen[abs($literal)]);
|
||||
|
||||
if ($num && 0 === --$num) {
|
||||
$learnedLiterals[0] = -abs($literal);
|
||||
if ($literal < 0) {
|
||||
$this->testFlagLearnedPositiveLiteral = true;
|
||||
}
|
||||
$learnedLiterals[0] = -$literal;
|
||||
|
||||
if (!$l1num) {
|
||||
break 2;
|
||||
|
@ -509,9 +525,8 @@ class Solver
|
|||
*/
|
||||
private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule)
|
||||
{
|
||||
$why = spl_object_hash($conflictRule);
|
||||
|
||||
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
|
||||
$why = spl_object_hash($conflictRule);
|
||||
$learnedWhy = $this->learnedWhy[$why];
|
||||
$problemRules = $this->learnedPool[$learnedWhy];
|
||||
|
||||
|
@ -677,7 +692,7 @@ class Solver
|
|||
/**
|
||||
* @todo this makes $disableRules always false; determine the rationale and possibly remove dead code?
|
||||
*/
|
||||
$disableRules = array();
|
||||
$disableRules = false;
|
||||
|
||||
$level = 1;
|
||||
$systemLevel = $level + 1;
|
||||
|
@ -759,10 +774,19 @@ class Solver
|
|||
}
|
||||
|
||||
$rulesCount = count($this->rules);
|
||||
$pass = 1;
|
||||
|
||||
$this->io->writeError('Looking at all rules.', true, IOInterface::DEBUG);
|
||||
for ($i = 0, $n = 0; $n < $rulesCount; $i++, $n++) {
|
||||
if ($i == $rulesCount) {
|
||||
if (1 === $pass) {
|
||||
$this->io->writeError("Something's changed, looking at all rules again (pass #$pass)", false, IOInterface::DEBUG);
|
||||
} else {
|
||||
$this->io->overwriteError("Something's changed, looking at all rules again (pass #$pass)", false, null, IOInterface::DEBUG);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$pass++;
|
||||
}
|
||||
|
||||
$rule = $this->rules->ruleById[$i];
|
||||
|
@ -782,14 +806,14 @@ class Solver
|
|||
//
|
||||
foreach ($literals as $literal) {
|
||||
if ($literal <= 0) {
|
||||
if (!$this->decisions->decidedInstall(abs($literal))) {
|
||||
if (!$this->decisions->decidedInstall($literal)) {
|
||||
continue 2; // next rule
|
||||
}
|
||||
} else {
|
||||
if ($this->decisions->decidedInstall(abs($literal))) {
|
||||
if ($this->decisions->decidedInstall($literal)) {
|
||||
continue 2; // next rule
|
||||
}
|
||||
if ($this->decisions->undecided(abs($literal))) {
|
||||
if ($this->decisions->undecided($literal)) {
|
||||
$decisionQueue[] = $literal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ class SolverBugException extends \RuntimeException
|
|||
{
|
||||
parent::__construct(
|
||||
$message."\nThis exception was most likely caused by a bug in Composer.\n".
|
||||
"Please report the command you ran, the exact error you received, and your composer.json on https://github.com/composer/composer/issues - thank you!\n");
|
||||
"Please report the command you ran, the exact error you received, and your composer.json on https://github.com/composer/composer/issues - thank you!\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,13 @@ class SolverProblemsException extends \RuntimeException
|
|||
{
|
||||
protected $problems;
|
||||
protected $installedMap;
|
||||
protected $learnedPool;
|
||||
|
||||
public function __construct(array $problems, array $installedMap)
|
||||
public function __construct(array $problems, array $installedMap, array $learnedPool)
|
||||
{
|
||||
$this->problems = $problems;
|
||||
$this->installedMap = $installedMap;
|
||||
$this->learnedPool = $learnedPool;
|
||||
|
||||
parent::__construct($this->createMessage(), 2);
|
||||
}
|
||||
|
@ -35,7 +37,7 @@ class SolverProblemsException extends \RuntimeException
|
|||
$text = "\n";
|
||||
$hasExtensionProblems = false;
|
||||
foreach ($this->problems as $i => $problem) {
|
||||
$text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n";
|
||||
$text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap, $this->learnedPool)."\n";
|
||||
|
||||
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
|
||||
$hasExtensionProblems = true;
|
||||
|
|
|
@ -49,7 +49,7 @@ class Transaction
|
|||
$package = $this->pool->literalToPackage($literal);
|
||||
|
||||
// wanted & installed || !wanted & !installed
|
||||
if (($literal > 0) == (isset($this->installedMap[$package->id]))) {
|
||||
if (($literal > 0) == isset($this->installedMap[$package->id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,20 +27,26 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws \RuntimeException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function download(PackageInterface $package, $path, $output = true)
|
||||
public function install(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
$fileName = parent::download($package, $path, $output);
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): Extracting archive");
|
||||
} else {
|
||||
$this->io->writeError('Extracting archive', false);
|
||||
}
|
||||
|
||||
$this->io->writeError(' Extracting archive', false, IOInterface::VERBOSE);
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
try {
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
try {
|
||||
$this->extract($fileName, $temporaryDir);
|
||||
$this->extract($package, $fileName, $temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
// remove cache if the file was corrupted
|
||||
parent::clearLastCacheWrite($package);
|
||||
|
@ -49,10 +55,25 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
|
||||
$this->filesystem->unlink($fileName);
|
||||
|
||||
$contentDir = $this->getFolderContent($temporaryDir);
|
||||
$renameAsOne = false;
|
||||
if (!file_exists($path) || ($this->filesystem->isDirEmpty($path) && $this->filesystem->removeDirectory($path))) {
|
||||
$renameAsOne = true;
|
||||
}
|
||||
|
||||
$contentDir = $this->getFolderContent($temporaryDir);
|
||||
$singleDirAtTopLevel = 1 === count($contentDir) && is_dir(reset($contentDir));
|
||||
|
||||
if ($renameAsOne) {
|
||||
// if the target $path is clear, we can rename the whole package in one go instead of looping over the contents
|
||||
if ($singleDirAtTopLevel) {
|
||||
$extractedDir = (string) reset($contentDir);
|
||||
} else {
|
||||
$extractedDir = $temporaryDir;
|
||||
}
|
||||
$this->filesystem->rename($extractedDir, $path);
|
||||
} else {
|
||||
// only one dir in the archive, extract its contents out of it
|
||||
if (1 === count($contentDir) && is_dir(reset($contentDir))) {
|
||||
if ($singleDirAtTopLevel) {
|
||||
$contentDir = $this->getFolderContent((string) reset($contentDir));
|
||||
}
|
||||
|
||||
|
@ -61,6 +82,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
$file = (string) $file;
|
||||
$this->filesystem->rename($file, $path . '/' . basename($file));
|
||||
}
|
||||
}
|
||||
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir').'/composer/')) {
|
||||
|
@ -73,24 +95,12 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
// clean up
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
|
||||
// retry downloading if we have an invalid zip file
|
||||
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
|
||||
$this->io->writeError('');
|
||||
if ($this->io->isDebug()) {
|
||||
$this->io->writeError(' Invalid zip file ('.$e->getMessage().'), retrying...');
|
||||
} else {
|
||||
$this->io->writeError(' Invalid zip file, retrying...');
|
||||
}
|
||||
usleep(500000);
|
||||
continue;
|
||||
if (file_exists($fileName)) {
|
||||
$this->filesystem->unlink($fileName);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,33 +108,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
*/
|
||||
protected function getFileName(PackageInterface $package, $path)
|
||||
{
|
||||
return rtrim($path.'/'.md5($path.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function processUrl(PackageInterface $package, $url)
|
||||
{
|
||||
if ($package->getDistReference() && strpos($url, 'github.com')) {
|
||||
if (preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$}i', $url, $match)) {
|
||||
// update legacy github archives to API calls with the proper reference
|
||||
$url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
|
||||
} elseif ($package->getDistReference() && preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$}i', $url, $match)) {
|
||||
// update current github web archives to API calls with the proper reference
|
||||
$url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
|
||||
} elseif ($package->getDistReference() && preg_match('{^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$}i', $url, $match)) {
|
||||
// update api archives to the proper reference
|
||||
$url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
|
||||
}
|
||||
} elseif ($package->getDistReference() && strpos($url, 'bitbucket.org')) {
|
||||
if (preg_match('{^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$}i', $url, $match)) {
|
||||
// update Bitbucket archives to the proper reference
|
||||
$url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $package->getDistReference() . '.' . $match[4];
|
||||
}
|
||||
}
|
||||
|
||||
return parent::processUrl($package, $url);
|
||||
return rtrim($path.'_'.md5($path.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +119,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
*
|
||||
* @throws \UnexpectedValueException If can not extract downloaded file to path
|
||||
*/
|
||||
abstract protected function extract($file, $path);
|
||||
abstract protected function extract(PackageInterface $package, $file, $path);
|
||||
|
||||
/**
|
||||
* Returns the folder content, excluding dotfiles
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\Downloader;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Downloaders manager.
|
||||
|
@ -24,6 +25,7 @@ use Composer\Util\Filesystem;
|
|||
class DownloadManager
|
||||
{
|
||||
private $io;
|
||||
private $httpDownloader;
|
||||
private $preferDist = false;
|
||||
private $preferSource = false;
|
||||
private $packagePreferences = array();
|
||||
|
@ -83,22 +85,6 @@ class DownloadManager
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to output download progress information for all registered
|
||||
* downloaders
|
||||
*
|
||||
* @param bool $outputProgress
|
||||
* @return DownloadManager
|
||||
*/
|
||||
public function setOutputProgress($outputProgress)
|
||||
{
|
||||
foreach ($this->downloaders as $downloader) {
|
||||
$downloader->setOutputProgress($outputProgress);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets installer downloader for a specific installation type.
|
||||
*
|
||||
|
@ -140,7 +126,7 @@ class DownloadManager
|
|||
* wrong type
|
||||
* @return DownloaderInterface|null
|
||||
*/
|
||||
public function getDownloaderForInstalledPackage(PackageInterface $package)
|
||||
public function getDownloaderForPackage(PackageInterface $package)
|
||||
{
|
||||
$installationSource = $package->getInstallationSource();
|
||||
|
||||
|
@ -154,77 +140,112 @@ class DownloadManager
|
|||
$downloader = $this->getDownloader($package->getSourceType());
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
'Package '.$package.' seems not been installed properly'
|
||||
'Package '.$package.' does not have an installation source set'
|
||||
);
|
||||
}
|
||||
|
||||
if ($installationSource !== $downloader->getInstallationSource()) {
|
||||
throw new \LogicException(sprintf(
|
||||
'Downloader "%s" is a %s type downloader and can not be used to download %s',
|
||||
get_class($downloader), $downloader->getInstallationSource(), $installationSource
|
||||
'Downloader "%s" is a %s type downloader and can not be used to download %s for package %s',
|
||||
get_class($downloader),
|
||||
$downloader->getInstallationSource(),
|
||||
$installationSource,
|
||||
$package
|
||||
));
|
||||
}
|
||||
|
||||
return $downloader;
|
||||
}
|
||||
|
||||
public function getDownloaderType(DownloaderInterface $downloader)
|
||||
{
|
||||
return array_search($downloader, $this->downloaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads package into target dir.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
* @param bool $preferSource prefer installation from source
|
||||
* @param PackageInterface $prevPackage previous package instance in case of updates
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @throws \InvalidArgumentException if package have no urls to download from
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function download(PackageInterface $package, $targetDir, $preferSource = null)
|
||||
public function download(PackageInterface $package, $targetDir, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$preferSource = null !== $preferSource ? $preferSource : $this->preferSource;
|
||||
$sourceType = $package->getSourceType();
|
||||
$distType = $package->getDistType();
|
||||
$this->filesystem->ensureDirectoryExists(dirname($targetDir));
|
||||
|
||||
$sources = array();
|
||||
if ($sourceType) {
|
||||
$sources[] = 'source';
|
||||
}
|
||||
if ($distType) {
|
||||
$sources[] = 'dist';
|
||||
}
|
||||
$sources = $this->getAvailableSources($package, $prevPackage);
|
||||
|
||||
if (empty($sources)) {
|
||||
throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
|
||||
}
|
||||
$io = $this->io;
|
||||
$self = $this;
|
||||
|
||||
if (!$preferSource && ($this->preferDist || 'dist' === $this->resolvePackageInstallPreference($package))) {
|
||||
$sources = array_reverse($sources);
|
||||
}
|
||||
|
||||
$this->filesystem->ensureDirectoryExists($targetDir);
|
||||
|
||||
foreach ($sources as $i => $source) {
|
||||
if (isset($e)) {
|
||||
$this->io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
|
||||
$download = function ($retry = false) use (&$sources, $io, $package, $self, $targetDir, &$download) {
|
||||
$source = array_shift($sources);
|
||||
if ($retry) {
|
||||
$io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
|
||||
}
|
||||
$package->setInstallationSource($source);
|
||||
try {
|
||||
$downloader = $this->getDownloaderForInstalledPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->download($package, $targetDir);
|
||||
|
||||
$downloader = $self->getDownloaderForPackage($package);
|
||||
if (!$downloader) {
|
||||
return \React\Promise\resolve();
|
||||
}
|
||||
break;
|
||||
} catch (\RuntimeException $e) {
|
||||
if ($i === count($sources) - 1) {
|
||||
|
||||
$handleError = function ($e) use ($sources, $source, $package, $io, $download) {
|
||||
if ($e instanceof \RuntimeException) {
|
||||
if (!$sources) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->io->writeError(
|
||||
$io->writeError(
|
||||
' <warning>Failed to download '.
|
||||
$package->getPrettyName().
|
||||
' from ' . $source . ': '.
|
||||
$e->getMessage().'</warning>'
|
||||
);
|
||||
|
||||
return $download(true);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
};
|
||||
|
||||
try {
|
||||
$result = $downloader->download($package, $targetDir);
|
||||
} catch (\Exception $e) {
|
||||
return $handleError($e);
|
||||
}
|
||||
if (!$result instanceof PromiseInterface) {
|
||||
return \React\Promise\resolve($result);
|
||||
}
|
||||
|
||||
$res = $result->then(function ($res) {
|
||||
return $res;
|
||||
}, $handleError);
|
||||
|
||||
return $res;
|
||||
};
|
||||
|
||||
return $download();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs package into target dir.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
*
|
||||
* @throws \InvalidArgumentException if package have no urls to download from
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function install(PackageInterface $package, $targetDir)
|
||||
{
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->install($package, $targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,31 +260,23 @@ class DownloadManager
|
|||
*/
|
||||
public function update(PackageInterface $initial, PackageInterface $target, $targetDir)
|
||||
{
|
||||
$downloader = $this->getDownloaderForInstalledPackage($initial);
|
||||
$downloader = $this->getDownloaderForPackage($target);
|
||||
$initialDownloader = $this->getDownloaderForPackage($initial);
|
||||
|
||||
// no downloaders present means update from metapackage to metapackage, nothing to do
|
||||
if (!$initialDownloader && !$downloader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have a downloader present before, but not after, the package became a metapackage and its files should be removed
|
||||
if (!$downloader) {
|
||||
$initialDownloader->remove($initial, $targetDir);
|
||||
return;
|
||||
}
|
||||
|
||||
$installationSource = $initial->getInstallationSource();
|
||||
|
||||
if ('dist' === $installationSource) {
|
||||
$initialType = $initial->getDistType();
|
||||
$targetType = $target->getDistType();
|
||||
} else {
|
||||
$initialType = $initial->getSourceType();
|
||||
$targetType = $target->getSourceType();
|
||||
}
|
||||
|
||||
// upgrading from a dist stable package to a dev package, force source reinstall
|
||||
if ($target->isDev() && 'dist' === $installationSource) {
|
||||
$downloader->remove($initial, $targetDir);
|
||||
$this->download($target, $targetDir);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$initialType = $this->getDownloaderType($initialDownloader);
|
||||
$targetType = $this->getDownloaderType($downloader);
|
||||
if ($initialType === $targetType) {
|
||||
$target->setInstallationSource($installationSource);
|
||||
try {
|
||||
$downloader->update($initial, $target, $targetDir);
|
||||
|
||||
|
@ -279,8 +292,10 @@ class DownloadManager
|
|||
}
|
||||
}
|
||||
|
||||
$downloader->remove($initial, $targetDir);
|
||||
$this->download($target, $targetDir, 'source' === $installationSource);
|
||||
// if downloader type changed, or update failed and user asks for reinstall,
|
||||
// we wipe the dir and do a new install instead of updating it
|
||||
$initialDownloader->remove($initial, $targetDir);
|
||||
$this->install($target, $targetDir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,7 +306,7 @@ class DownloadManager
|
|||
*/
|
||||
public function remove(PackageInterface $package, $targetDir)
|
||||
{
|
||||
$downloader = $this->getDownloaderForInstalledPackage($package);
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->remove($package, $targetDir);
|
||||
}
|
||||
|
@ -319,4 +334,48 @@ class DownloadManager
|
|||
|
||||
return $package->isDev() ? 'source' : 'dist';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getAvailableSources(PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$sourceType = $package->getSourceType();
|
||||
$distType = $package->getDistType();
|
||||
|
||||
// add source before dist by default
|
||||
$sources = array();
|
||||
if ($sourceType) {
|
||||
$sources[] = 'source';
|
||||
}
|
||||
if ($distType) {
|
||||
$sources[] = 'dist';
|
||||
}
|
||||
|
||||
if (empty($sources)) {
|
||||
throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
|
||||
}
|
||||
|
||||
if (
|
||||
$prevPackage
|
||||
// if we are updating, we want to keep the same source as the previously installed package (if available in the new one)
|
||||
&& in_array($prevPackage->getInstallationSource(), $sources, true)
|
||||
// unless the previous package was stable dist (by default) and the new package is dev, then we allow the new default to take over
|
||||
&& !(!$prevPackage->isDev() && $prevPackage->getInstallationSource() === 'dist' && $package->isDev())
|
||||
) {
|
||||
$prevSource = $prevPackage->getInstallationSource();
|
||||
usort($sources, function ($a, $b) use ($prevSource) {
|
||||
return $a === $prevSource ? -1 : 1;
|
||||
});
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
// reverse sources in case dist is the preferred source for this package
|
||||
if (!$this->preferSource && ($this->preferDist || 'dist' === $this->resolvePackageInstallPreference($package))) {
|
||||
$sources = array_reverse($sources);
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Downloader;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Downloader interface.
|
||||
|
@ -29,13 +30,20 @@ interface DownloaderInterface
|
|||
*/
|
||||
public function getInstallationSource();
|
||||
|
||||
/**
|
||||
* This should do any network-related tasks to prepare for install/update
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function download(PackageInterface $package, $path);
|
||||
|
||||
/**
|
||||
* Downloads specific package into specific folder.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
*/
|
||||
public function download(PackageInterface $package, $path);
|
||||
public function install(PackageInterface $package, $path);
|
||||
|
||||
/**
|
||||
* Updates specific package in specific folder from initial to target version.
|
||||
|
@ -53,12 +61,4 @@ interface DownloaderInterface
|
|||
* @param string $path download path
|
||||
*/
|
||||
public function remove(PackageInterface $package, $path);
|
||||
|
||||
/**
|
||||
* Sets whether to output download progress information or not
|
||||
*
|
||||
* @param bool $outputProgress
|
||||
* @return DownloaderInterface
|
||||
*/
|
||||
public function setOutputProgress($outputProgress);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,17 @@ use Composer\Config;
|
|||
use Composer\Cache;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\Comparer\Comparer;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PreFileDownloadEvent;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\Url as UrlUtil;
|
||||
use Composer\Downloader\TransportException;
|
||||
|
||||
/**
|
||||
* Base downloader for files
|
||||
|
@ -31,15 +36,17 @@ use Composer\Util\RemoteFilesystem;
|
|||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class FileDownloader implements DownloaderInterface
|
||||
class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
||||
{
|
||||
protected $io;
|
||||
protected $config;
|
||||
protected $rfs;
|
||||
protected $httpDownloader;
|
||||
protected $filesystem;
|
||||
protected $cache;
|
||||
protected $outputProgress = true;
|
||||
private $lastCacheWrites = array();
|
||||
/**
|
||||
* @private this is only public for php 5.3 support in closures
|
||||
*/
|
||||
public $lastCacheWrites = array();
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
|
@ -47,17 +54,17 @@ class FileDownloader implements DownloaderInterface
|
|||
*
|
||||
* @param IOInterface $io The IO instance
|
||||
* @param Config $config The config
|
||||
* @param HttpDownloader $httpDownloader The remote filesystem
|
||||
* @param EventDispatcher $eventDispatcher The event dispatcher
|
||||
* @param Cache $cache Optional cache instance
|
||||
* @param RemoteFilesystem $rfs The remote filesystem
|
||||
* @param Cache $cache Cache instance
|
||||
* @param Filesystem $filesystem The filesystem
|
||||
*/
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, RemoteFilesystem $rfs = null, Filesystem $filesystem = null)
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $filesystem = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->rfs = $rfs ?: Factory::createRemoteFilesystem($this->io, $config);
|
||||
$this->httpDownloader = $httpDownloader;
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
$this->cache = $cache;
|
||||
|
||||
|
@ -83,120 +90,157 @@ class FileDownloader implements DownloaderInterface
|
|||
throw new \InvalidArgumentException('The given package is missing url information');
|
||||
}
|
||||
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): ", false);
|
||||
}
|
||||
|
||||
$retries = 3;
|
||||
$urls = $package->getDistUrls();
|
||||
while ($url = array_shift($urls)) {
|
||||
try {
|
||||
$fileName = $this->doDownload($package, $path, $url);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if ($this->io->isDebug()) {
|
||||
$this->io->writeError('');
|
||||
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
|
||||
} elseif (count($urls)) {
|
||||
$this->io->writeError('');
|
||||
$this->io->writeError(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')', false);
|
||||
foreach ($urls as $index => $url) {
|
||||
$processedUrl = $this->processUrl($package, $url);
|
||||
$urls[$index] = array(
|
||||
'base' => $url,
|
||||
'processed' => $processedUrl,
|
||||
'cacheKey' => $this->getCacheKey($package, $processedUrl)
|
||||
);
|
||||
}
|
||||
|
||||
if (!count($urls)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
$io = $this->io;
|
||||
$cache = $this->cache;
|
||||
$httpDownloader = $this->httpDownloader;
|
||||
$eventDispatcher = $this->eventDispatcher;
|
||||
$filesystem = $this->filesystem;
|
||||
$self = $this;
|
||||
|
||||
$accept = null;
|
||||
$reject = null;
|
||||
$download = function () use ($io, $output, $httpDownloader, $cache, $eventDispatcher, $package, $fileName, $path, &$urls, &$accept, &$reject) {
|
||||
$url = reset($urls);
|
||||
|
||||
if ($eventDispatcher) {
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $httpDownloader, $url['processed']);
|
||||
$eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
}
|
||||
|
||||
$checksum = $package->getDistSha1Checksum();
|
||||
$cacheKey = $url['cacheKey'];
|
||||
|
||||
// use from cache if it is present and has a valid checksum or we have no checksum to check against
|
||||
if ($cache && (!$checksum || $checksum === $cache->sha1($cacheKey)) && $cache->copyTo($cacheKey, $fileName)) {
|
||||
if ($output) {
|
||||
$this->io->writeError('');
|
||||
$io->writeError(" - Loading <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>) from cache");
|
||||
}
|
||||
$result = \React\Promise\resolve($fileName);
|
||||
} else {
|
||||
if ($output) {
|
||||
$io->writeError(" - Downloading <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
}
|
||||
|
||||
$result = $httpDownloader->addCopy($url['processed'], $fileName, $package->getTransportOptions())
|
||||
->then($accept, $reject);
|
||||
}
|
||||
|
||||
return $result->then(function ($result) use ($fileName, $checksum, $url) {
|
||||
// in case of retry, the first call's Promise chain finally calls this twice at the end,
|
||||
// once with $result being the returned $fileName from $accept, and then once for every
|
||||
// failed request with a null result, which can be skipped.
|
||||
if (null === $result) {
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function doDownload(PackageInterface $package, $path, $url)
|
||||
{
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
$processedUrl = $this->processUrl($package, $url);
|
||||
$hostname = parse_url($processedUrl, PHP_URL_HOST);
|
||||
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl);
|
||||
if ($this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
}
|
||||
$rfs = $preFileDownloadEvent->getRemoteFilesystem();
|
||||
|
||||
try {
|
||||
$checksum = $package->getDistSha1Checksum();
|
||||
$cacheKey = $this->getCacheKey($package, $processedUrl);
|
||||
|
||||
// download if we don't have it in cache or the cache is invalidated
|
||||
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
|
||||
if (!$this->outputProgress) {
|
||||
$this->io->writeError('Downloading', false);
|
||||
}
|
||||
|
||||
// try to download 3 times then fail hard
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions());
|
||||
break;
|
||||
} catch (TransportException $e) {
|
||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||
if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) {
|
||||
throw $e;
|
||||
}
|
||||
$this->io->writeError('');
|
||||
$this->io->writeError(' Download failed, retrying...', true, IOInterface::VERBOSE);
|
||||
usleep(500000);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->outputProgress) {
|
||||
$this->io->writeError(' (<comment>100%</comment>)', false);
|
||||
}
|
||||
|
||||
if ($this->cache) {
|
||||
$this->lastCacheWrites[$package->getName()] = $cacheKey;
|
||||
$this->cache->copyFrom($cacheKey, $fileName);
|
||||
}
|
||||
} else {
|
||||
$this->io->writeError('Loading from cache', false);
|
||||
}
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
|
||||
throw new \UnexpectedValueException($url['base'].' could not be saved to '.$fileName.', make sure the'
|
||||
.' directory is writable and you have internet connectivity');
|
||||
}
|
||||
|
||||
if ($checksum && hash_file('sha1', $fileName) !== $checksum) {
|
||||
throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// clean up
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->clearLastCacheWrite($package);
|
||||
throw $e;
|
||||
throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url['base'].')');
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
});
|
||||
};
|
||||
|
||||
$accept = function ($response) use ($io, $cache, $package, $fileName, $path, $self, &$urls) {
|
||||
$url = reset($urls);
|
||||
$cacheKey = $url['cacheKey'];
|
||||
|
||||
if ($cache) {
|
||||
$self->lastCacheWrites[$package->getName()] = $cacheKey;
|
||||
$cache->copyFrom($cacheKey, $fileName);
|
||||
}
|
||||
|
||||
$response->collect();
|
||||
|
||||
return $fileName;
|
||||
};
|
||||
|
||||
$reject = function ($e) use ($io, &$urls, $download, $fileName, $path, $package, &$retries, $filesystem, $self) {
|
||||
// clean up
|
||||
if (file_exists($fileName)) {
|
||||
$filesystem->unlink($fileName);
|
||||
}
|
||||
$self->clearLastCacheWrite($package);
|
||||
|
||||
if ($e instanceof TransportException) {
|
||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||
if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) {
|
||||
$retries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// special error code returned when network is being artificially disabled
|
||||
if ($e instanceof TransportException && $e->getStatusCode() === 499) {
|
||||
$retries = 0;
|
||||
$urls = array();
|
||||
}
|
||||
|
||||
if ($retries) {
|
||||
usleep(500000);
|
||||
$retries--;
|
||||
|
||||
return $download();
|
||||
}
|
||||
|
||||
array_shift($urls);
|
||||
if ($urls) {
|
||||
if ($io->isDebug()) {
|
||||
$io->writeError(' Failed downloading '.$package->getName().': ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
|
||||
$io->writeError(' Trying the next URL for '.$package->getName());
|
||||
} elseif (count($urls)) {
|
||||
$io->writeError(' Failed downloading '.$package->getName().', trying the next URL ('.$e->getCode().': '.$e->getMessage().')');
|
||||
}
|
||||
|
||||
$retries = 3;
|
||||
usleep(100000);
|
||||
|
||||
return $download();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
};
|
||||
|
||||
return $download();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setOutputProgress($outputProgress)
|
||||
public function install(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
$this->outputProgress = $outputProgress;
|
||||
|
||||
return $this;
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
}
|
||||
|
||||
protected function clearLastCacheWrite(PackageInterface $package)
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
$this->filesystem->rename($this->getFileName($package, $path), $path . pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO mark private in v3
|
||||
* @protected This is public due to PHP 5.3
|
||||
*/
|
||||
public function clearLastCacheWrite(PackageInterface $package)
|
||||
{
|
||||
if ($this->cache && isset($this->lastCacheWrites[$package->getName()])) {
|
||||
$this->cache->remove($this->lastCacheWrites[$package->getName()]);
|
||||
|
@ -210,13 +254,14 @@ class FileDownloader implements DownloaderInterface
|
|||
public function update(PackageInterface $initial, PackageInterface $target, $path)
|
||||
{
|
||||
$name = $target->getName();
|
||||
$from = $initial->getPrettyVersion();
|
||||
$to = $target->getPrettyVersion();
|
||||
$from = $initial->getFullPrettyVersion();
|
||||
$to = $target->getFullPrettyVersion();
|
||||
|
||||
$this->io->writeError(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>): ", false);
|
||||
$actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Updating' : 'Downgrading';
|
||||
$this->io->writeError(" - " . $actionName . " <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>): ", false);
|
||||
|
||||
$this->remove($initial, $path, false);
|
||||
$this->download($target, $path, false);
|
||||
$this->install($target, $path, false);
|
||||
|
||||
$this->io->writeError('');
|
||||
}
|
||||
|
@ -243,7 +288,7 @@ class FileDownloader implements DownloaderInterface
|
|||
*/
|
||||
protected function getFileName(PackageInterface $package, $path)
|
||||
{
|
||||
return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
|
||||
return $path.'_'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,6 +305,10 @@ class FileDownloader implements DownloaderInterface
|
|||
throw new \RuntimeException('You must enable the openssl extension to download files via https');
|
||||
}
|
||||
|
||||
if ($package->getDistReference()) {
|
||||
$url = UrlUtil::updateDistReference($this->config, $url, $package->getDistReference());
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
@ -273,4 +322,39 @@ class FileDownloader implements DownloaderInterface
|
|||
|
||||
return $package->getName().'/'.$cacheKey.'.'.$package->getDistType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function getLocalChanges(PackageInterface $package, $targetDir)
|
||||
{
|
||||
$prevIO = $this->io;
|
||||
|
||||
$this->io = new NullIO;
|
||||
$this->io->loadConfiguration($this->config);
|
||||
$e = null;
|
||||
|
||||
try {
|
||||
$res = $this->download($package, $targetDir.'_compare', false);
|
||||
$this->httpDownloader->wait();
|
||||
$res = $this->install($package, $targetDir.'_compare', false);
|
||||
|
||||
$comparer = new Comparer();
|
||||
$comparer->setSource($targetDir.'_compare');
|
||||
$comparer->setUpdate($targetDir);
|
||||
$comparer->doCompare();
|
||||
$output = $comparer->getChanged(true, true);
|
||||
$this->filesystem->removeDirectory($targetDir.'_compare');
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
$this->io = $prevIO;
|
||||
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return trim($output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class FossilDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doDownload(PackageInterface $package, $path, $url)
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
// Ensure we are allowed to use this URL by config
|
||||
$this->config->prohibitUrlByConfig($url, $this->io);
|
||||
|
@ -36,7 +36,7 @@ class FossilDownloader extends VcsDownloader
|
|||
if (0 !== $this->process->execute($command, $ignoredOutput)) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
}
|
||||
$command = sprintf('fossil open %s', ProcessExecutor::escape($repoFile));
|
||||
$command = sprintf('fossil open %s --nested', ProcessExecutor::escape($repoFile));
|
||||
if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class FossilDownloader extends VcsDownloader
|
|||
*/
|
||||
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||
{
|
||||
$command = sprintf('fossil timeline -t ci -W 0 -n 0 before %s', $toReference);
|
||||
$command = sprintf('fossil timeline -t ci -W 0 -n 0 before %s', ProcessExecutor::escape($toReference));
|
||||
|
||||
if (0 !== $this->process->execute($command, $output, realpath($path))) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
|
|
|
@ -19,6 +19,7 @@ use Composer\Util\Filesystem;
|
|||
use Composer\Util\Git as GitUtil;
|
||||
use Composer\Util\Platform;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Cache;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -38,7 +39,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doDownload(PackageInterface $package, $path, $url)
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
GitUtil::cleanEnv();
|
||||
$path = $this->normalizePath($path);
|
||||
|
@ -51,7 +52,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
$msg = "Cloning ".$this->getShortHash($ref);
|
||||
|
||||
$command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer';
|
||||
if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=')) {
|
||||
if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) {
|
||||
$this->io->writeError('', true, IOInterface::DEBUG);
|
||||
$this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG);
|
||||
try {
|
||||
|
@ -361,7 +362,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
) {
|
||||
$command = sprintf('git checkout '.$force.'-B %s %s -- && git reset --hard %2$s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference));
|
||||
if (0 === $this->process->execute($command, $output, $path)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,14 +380,14 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
) {
|
||||
$command = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference));
|
||||
if (0 === $this->process->execute($command, $output, $path)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$command = sprintf($template, ProcessExecutor::escape($gitRef));
|
||||
if (0 === $this->process->execute($command, $output, $path)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// reference was not found (prints "fatal: reference is not a tree: $ref")
|
||||
|
@ -423,7 +424,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$command = sprintf('git log %s..%s --pretty=format:"%%h - %%an: %%s"', $fromReference, $toReference);
|
||||
$command = sprintf('git log %s..%s --pretty=format:"%%h - %%an: %%s"', ProcessExecutor::escape($fromReference), ProcessExecutor::escape($toReference));
|
||||
|
||||
if (0 !== $this->process->execute($command, $output, $path)) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
|
@ -433,7 +434,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param string $path
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function discardChanges($path)
|
||||
|
@ -447,7 +448,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param string $path
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function stashChanges($path)
|
||||
|
@ -461,7 +462,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param string $path
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function viewDiff($path)
|
||||
|
|
|
@ -18,7 +18,7 @@ use Composer\EventDispatcher\EventDispatcher;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Util\Platform;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,17 +28,19 @@ use Composer\IO\IOInterface;
|
|||
*/
|
||||
class GzipDownloader extends ArchiveDownloader
|
||||
{
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null, RemoteFilesystem $rfs = null)
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $eventDispatcher, $cache, $rfs);
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache);
|
||||
}
|
||||
|
||||
protected function extract($file, $path)
|
||||
protected function extract(PackageInterface $package, $file, $path)
|
||||
{
|
||||
$targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3));
|
||||
$filename = pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_FILENAME);
|
||||
$targetFilepath = $path . DIRECTORY_SEPARATOR . $filename;
|
||||
|
||||
// Try to use gunzip on *nix
|
||||
if (!Platform::isWindows()) {
|
||||
|
@ -63,14 +65,6 @@ class GzipDownloader extends ArchiveDownloader
|
|||
$this->extractUsingExt($file, $targetFilepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getFileName(PackageInterface $package, $path)
|
||||
{
|
||||
return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
|
||||
}
|
||||
|
||||
private function extractUsingExt($file, $targetFilepath)
|
||||
{
|
||||
$archiveFile = gzopen($file, 'rb');
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Downloader;
|
|||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Hg as HgUtils;
|
||||
|
||||
/**
|
||||
* @author Per Bernhardt <plb@webfactory.de>
|
||||
|
@ -23,18 +24,17 @@ class HgDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doDownload(PackageInterface $package, $path, $url)
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
// Ensure we are allowed to use this URL by config
|
||||
$this->config->prohibitUrlByConfig($url, $this->io);
|
||||
$hgUtils = new HgUtils($this->io, $this->config, $this->process);
|
||||
|
||||
$cloneCommand = function ($url) use ($path) {
|
||||
return sprintf('hg clone %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($path));
|
||||
};
|
||||
|
||||
$hgUtils->runCommand($cloneCommand, $url, $path);
|
||||
|
||||
$url = ProcessExecutor::escape($url);
|
||||
$ref = ProcessExecutor::escape($package->getSourceReference());
|
||||
$this->io->writeError("Cloning ".$package->getSourceReference());
|
||||
$command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
|
||||
if (0 !== $this->process->execute($command, $ignoredOutput)) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
}
|
||||
$command = sprintf('hg up %s', $ref);
|
||||
if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
|
@ -46,21 +46,20 @@ class HgDownloader extends VcsDownloader
|
|||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
// Ensure we are allowed to use this URL by config
|
||||
$this->config->prohibitUrlByConfig($url, $this->io);
|
||||
$hgUtils = new HgUtils($this->io, $this->config, $this->process);
|
||||
|
||||
$url = ProcessExecutor::escape($url);
|
||||
$ref = ProcessExecutor::escape($target->getSourceReference());
|
||||
$ref = $target->getSourceReference();
|
||||
$this->io->writeError(" Updating to ".$target->getSourceReference());
|
||||
|
||||
if (!$this->hasMetadataRepository($path)) {
|
||||
throw new \RuntimeException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
|
||||
}
|
||||
|
||||
$command = sprintf('hg pull %s && hg up %s', $url, $ref);
|
||||
if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
}
|
||||
$command = function ($url) use ($ref) {
|
||||
return sprintf('hg pull %s && hg up %s', ProcessExecutor::escape($url), ProcessExecutor::escape($ref));
|
||||
};
|
||||
|
||||
$hgUtils->runCommand($command, $url, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +81,7 @@ class HgDownloader extends VcsDownloader
|
|||
*/
|
||||
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||
{
|
||||
$command = sprintf('hg log -r %s:%s --style compact', $fromReference, $toReference);
|
||||
$command = sprintf('hg log -r %s:%s --style compact', ProcessExecutor::escape($fromReference), ProcessExecutor::escape($toReference));
|
||||
|
||||
if (0 !== $this->process->execute($command, $output, realpath($path))) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue