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,
|
||||
|
|
74
.travis.yml
74
.travis.yml
|
@ -1,7 +1,5 @@
|
|||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: trusty
|
||||
|
||||
git:
|
||||
|
@ -16,50 +14,62 @@ 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
|
||||
- echo memory_limit = -1 >> $INI
|
||||
# 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"
|
||||
# install dependencies using system provided composer binary
|
||||
- composer install $flags
|
||||
# install dependencies using composer from source
|
||||
- bin/composer install $flags
|
||||
# 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
|
||||
- bin/composer install $flags
|
||||
|
||||
before_script:
|
||||
# make sure git tests do not complain about user/email not being set
|
||||
- git config --global user.name travis-ci
|
||||
- git config --global user.email travis@example.com
|
||||
# make sure git tests do not complain about user/email not being set
|
||||
- git config --global user.name travis-ci
|
||||
- git config --global user.email travis@example.com
|
||||
|
||||
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);'
|
||||
# run test suite directories in parallel using GNU parallel
|
||||
- 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
|
||||
- php -d phar.readonly=0 bin/compile
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
|
@ -69,4 +79,4 @@ deploy:
|
|||
on:
|
||||
tags: true
|
||||
repo: composer/composer
|
||||
php: '7.1'
|
||||
php: '7.2'
|
||||
|
|
173
CHANGELOG.md
173
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
|
||||
|
@ -14,7 +163,7 @@
|
|||
### [1.5.6] - 2017-12-18
|
||||
|
||||
* Fixed root package version guessed when a tag is checked out
|
||||
* Fixed support for GitLab reposhosted on non-standard ports
|
||||
* Fixed support for GitLab repos hosted on non-standard ports
|
||||
* Fixed regression in require command when requiring unstable packages, part 3
|
||||
|
||||
### [1.5.5] - 2017-12-01
|
||||
|
@ -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": [],
|
||||
|
|
|
@ -10,11 +10,11 @@ Composer is **not** a package manager in the same sense as Yum or Apt are. Yes,
|
|||
it deals with "packages" or libraries, but it manages them on a per-project
|
||||
basis, installing them in a directory (e.g. `vendor`) inside your project. By
|
||||
default it does not install anything globally. Thus, it is a dependency
|
||||
manager. It does however support a "global" project for convenience via the
|
||||
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,14 +40,14 @@ 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
|
||||
|
||||
Composer offers a convenient installer that you can execute directly from the
|
||||
commandline. Feel free to [download this file](https://getcomposer.org/installer)
|
||||
command line. Feel free to [download this file](https://getcomposer.org/installer)
|
||||
or review it on [GitHub](https://github.com/composer/getcomposer.org/blob/master/web/installer)
|
||||
if you wish to know more about the inner workings of the installer. The source
|
||||
is plain PHP.
|
||||
|
@ -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
|
||||
|
@ -205,7 +205,7 @@ project to use the patched version. If the library is on GitHub (this is the
|
|||
case most of the time), you can simply fork it there and push your changes to
|
||||
your fork. After that you update the project's `composer.json`. All you have
|
||||
to do is add your fork as a repository and update the version constraint to
|
||||
point to your custom branch. In `composer.json`, you should prefix your custom
|
||||
point to your custom branch. In `composer.json`, you should prefix your custom
|
||||
branch name with `"dev-"`. For version constraint naming conventions see
|
||||
[Libraries](02-libraries.md) for more information.
|
||||
|
||||
|
@ -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
|
||||
|
@ -300,18 +301,15 @@ Please note:
|
|||
|
||||
The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer.
|
||||
|
||||
After creating an OAuth consumer in the BitBucket control panel, you need to setup your auth.json file with
|
||||
After creating an OAuth consumer in the BitBucket control panel, you need to setup your auth.json file with
|
||||
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"
|
||||
}
|
||||
"bitbucket-oauth": {
|
||||
"bitbucket.org": {
|
||||
"consumer-key": "myKey",
|
||||
"consumer-secret": "mySecret"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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": {
|
||||
|
@ -518,7 +516,7 @@ Typically you would leave the source part off, as you don't really need it.
|
|||
> reference you will have to delete the package to force an update, and will
|
||||
> have to deal with an unstable lock file.
|
||||
|
||||
The `"package"` key in a `package` repository may be set to an array to define multiple versions of a package:
|
||||
The `"package"` key in a `package` repository may be set to an array to define multiple versions of a package:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -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,19 +60,19 @@ 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`
|
||||
fields. Let's say you found a bug in the `monolog/monolog` package. You cloned
|
||||
[Monolog](https://github.com/Seldaek/monolog) on GitHub and fixed the issue in
|
||||
a branch named `bugfix`. Now you want to install that version of monolog in your
|
||||
[Monolog](https://github.com/Seldaek/monolog) on GitHub and fixed the issue in
|
||||
a branch named `bugfix`. Now you want to install that version of monolog in your
|
||||
local project.
|
||||
|
||||
You are using `symfony/monolog-bundle` which requires `monolog/monolog` version
|
||||
`1.*`. So you need your `dev-bugfix` to match that constraint.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +191,8 @@ with [`Composer\Composer`][4]'s internal state, by providing explicit extension
|
|||
for common plugin requirements.
|
||||
|
||||
Capable Plugins classes must implement the [`Composer\Plugin\Capable`][8] interface
|
||||
and declare their capabilities in the `getCapabilities()` method.
|
||||
This method must return an array, with the _key_ as a Composer Capability class name,
|
||||
and declare their capabilities in the `getCapabilities()` method.
|
||||
This method must return an array, with the _key_ as a Composer Capability class name,
|
||||
and the _value_ as the Plugin's own implementation class name of said Capability:
|
||||
|
||||
```php
|
||||
|
@ -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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# How do I install untrusted packages safely? Is it safe to run Composer as superuser or root?
|
||||
|
||||
Certain Composer commands, including `exec`, `install`, and `update` allow third party code to
|
||||
Certain Composer commands, including `exec`, `install`, and `update` allow third party code to
|
||||
execute on your system. This is from its "plugins" and "scripts" features. Plugins and scripts have
|
||||
full access to the user account which runs Composer. For this reason, it is strongly advised to
|
||||
**avoid running Composer as super-user/root**.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,11 +377,11 @@ class ClassLoader
|
|||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath.'\\';
|
||||
$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,11 +76,13 @@ class Cache
|
|||
|
||||
public function read($file)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $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 file_get_contents($this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -142,19 +149,21 @@ class Cache
|
|||
*/
|
||||
public function copyTo($file, $target)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
try {
|
||||
touch($this->root . $file, filemtime($this->root . $file), time());
|
||||
} catch (\ErrorException $e) {
|
||||
// fallback in case the above failed due to incorrect ownership
|
||||
// see https://github.com/composer/composer/issues/4070
|
||||
Silencer::call('touch', $this->root . $file);
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
try {
|
||||
touch($this->root . $file, filemtime($this->root . $file), time());
|
||||
} catch (\ErrorException $e) {
|
||||
// fallback in case the above failed due to incorrect ownership
|
||||
// see https://github.com/composer/composer/issues/4070
|
||||
Silencer::call('touch', $this->root . $file);
|
||||
}
|
||||
|
||||
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
|
||||
|
||||
return copy($this->root . $file, $target);
|
||||
}
|
||||
|
||||
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
|
||||
|
||||
return copy($this->root . $file, $target);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -167,9 +176,11 @@ class Cache
|
|||
|
||||
public function remove($file)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
return $this->filesystem->unlink($this->root . $file);
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $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,9 +228,11 @@ class Cache
|
|||
|
||||
public function sha1($file)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
return sha1_file($this->root . $file);
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
return sha1_file($this->root . $file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -226,9 +240,11 @@ class Cache
|
|||
|
||||
public function sha256($file)
|
||||
{
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
if ($this->enabled && file_exists($this->root . $file)) {
|
||||
return hash_file('sha256', $this->root . $file);
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $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,13 +326,16 @@ EOT
|
|||
}
|
||||
|
||||
// handler Ctrl+C for unix-like systems
|
||||
if (function_exists('pcntl_signal')) {
|
||||
declare(ticks=100);
|
||||
pcntl_signal(SIGINT, function () use ($directory) {
|
||||
$fs = new Filesystem();
|
||||
$fs->removeDirectory($directory);
|
||||
exit(130);
|
||||
});
|
||||
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($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://repo.packagist.org',
|
||||
));
|
||||
}
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, array(
|
||||
'type' => 'composer',
|
||||
'url' => 'https://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');
|
||||
|
||||
$cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin)));
|
||||
// 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
|
||||
)
|
||||
;
|
||||
|
@ -93,12 +96,25 @@ EOT
|
|||
if (isset($composer[$type][$package])) {
|
||||
$json->removeLink($type, $composer[$type][$package]);
|
||||
} elseif (isset($composer[$altType][$package])) {
|
||||
$io->writeError('<warning>'.$composer[$altType][$package].' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
|
||||
$io->writeError('<warning>' . $composer[$altType][$package] . ' 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)) {
|
||||
if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) {
|
||||
$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,16 +64,18 @@ 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
|
||||
If you do not specify a package, composer will prompt you to search for a package, and given results, provide a list of
|
||||
matches to require.
|
||||
|
||||
If you do not specify a version constraint, composer will choose a suitable one based on the available package versions.
|
||||
|
||||
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,10 +116,27 @@ 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
|
||||
|
@ -183,8 +196,11 @@ r/TU7BQQIzsZgAiqOGXvVklIgAMiV0iucgf3rNBLjjeNEwNSTTG9F0CtQ+7JLwaE
|
|||
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
|
||||
|
@ -200,12 +216,12 @@ TzCFWGk/HM6a4f0IzBWbJ5ot0PIi4amk07IotBXDWwqDiQTwyuGCym5EqWQ2BD95
|
|||
RGv89BPD+2DLnJysngsvVaUCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
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,23 +354,17 @@ EOT
|
|||
ksort($packages[$type]);
|
||||
|
||||
$nameLength = $versionLength = $latestLength = 0;
|
||||
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()));
|
||||
if ($showLatest && $showVersion) {
|
||||
foreach ($packages[$type] as $package) {
|
||||
if (is_object($package)) {
|
||||
$latestPackage = $this->findLatestPackage($package, $composer, $phpVersion, $showMinorOnly);
|
||||
if ($latestPackage === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestPackages[$package->getPrettyName()] = $latestPackage;
|
||||
}
|
||||
} else {
|
||||
$nameLength = max($nameLength, strlen($package));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
$treeBar = '├';
|
||||
$j = 0;
|
||||
if (isset($package['requires'])) {
|
||||
$requires = $package['requires'];
|
||||
$treeBar = '├';
|
||||
$j = 0;
|
||||
$total = count($requires);
|
||||
foreach ($requires as $require) {
|
||||
$requireName = $require['name'];
|
||||
$j++;
|
||||
if ($j === $total) {
|
||||
$treeBar = '└';
|
||||
}
|
||||
$level = 1;
|
||||
$color = $this->colors[$level];
|
||||
$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);
|
||||
|
||||
$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 $requireName => $require) {
|
||||
$j++;
|
||||
if ($j == 0) {
|
||||
$this->writeTreeLine($treeBar);
|
||||
foreach ($requires as $require) {
|
||||
$currentTree = $packagesInTree;
|
||||
$i++;
|
||||
if ($i === $total) {
|
||||
$treeBar = $previousTreeBar . ' └';
|
||||
}
|
||||
if ($j == $total) {
|
||||
$treeBar = '└';
|
||||
}
|
||||
$level = 1;
|
||||
$color = $this->colors[$level];
|
||||
$info = sprintf('%s──<%s>%s</%s> %s', $treeBar, $color, $requireName, $color, $require->getPrettyConstraint());
|
||||
$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);
|
||||
$packagesInTree = array($package->getName(), $requireName);
|
||||
|
||||
$this->displayTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree, $treeBar, $level + 1);
|
||||
$currentTree[] = $require['name'];
|
||||
$this->displayTree($require, $currentTree, $treeBar, $level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -749,44 +877,51 @@ EOT
|
|||
/**
|
||||
* Display a package tree
|
||||
*
|
||||
* @param string $name
|
||||
* @param PackageInterface|string $package
|
||||
* @param RepositoryInterface $installedRepo
|
||||
* @param RepositoryInterface $distantRepos
|
||||
* @param array $packagesInTree
|
||||
* @param string $previousTreeBar
|
||||
* @param int $level
|
||||
* @param string $name
|
||||
* @param PackageInterface|string $package
|
||||
* @param RepositoryInterface $installedRepo
|
||||
* @param RepositoryInterface $distantRepos
|
||||
* @param array $packagesInTree
|
||||
* @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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ class GenericRule extends Rule
|
|||
protected $literals;
|
||||
|
||||
/**
|
||||
* @param array $literals
|
||||
* @param int $reason A RULE_* constant describing the reason for generating this rule
|
||||
* @param Link|PackageInterface $reasonData
|
||||
* @param array $job The job this rule was created from
|
||||
* @param array $literals
|
||||
* @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 setPackages(array $packages, array $priorities = array())
|
||||
{
|
||||
$id = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setWhitelist($whitelist)
|
||||
public function getPriority($id)
|
||||
{
|
||||
$this->whitelist = $whitelist;
|
||||
$this->providerCache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++);
|
||||
$this->packages[] = $package;
|
||||
$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)
|
||||
{
|
||||
$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,32 +27,53 @@ 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)
|
||||
{
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): Extracting archive");
|
||||
} else {
|
||||
$this->io->writeError('Extracting archive', false);
|
||||
}
|
||||
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
$fileName = parent::download($package, $path, $output);
|
||||
|
||||
$this->io->writeError(' Extracting archive', false, IOInterface::VERBOSE);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
try {
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
try {
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
try {
|
||||
$this->extract($fileName, $temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
// remove cache if the file was corrupted
|
||||
parent::clearLastCacheWrite($package);
|
||||
throw $e;
|
||||
$this->extract($package, $fileName, $temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
// remove cache if the file was corrupted
|
||||
parent::clearLastCacheWrite($package);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->filesystem->unlink($fileName);
|
||||
|
||||
$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->unlink($fileName);
|
||||
|
||||
$contentDir = $this->getFolderContent($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,35 +82,24 @@ 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/')) {
|
||||
$this->filesystem->removeDirectory($this->config->get('vendor-dir').'/composer/');
|
||||
}
|
||||
if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir'))) {
|
||||
$this->filesystem->removeDirectory($this->config->get('vendor-dir'));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
break;
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir').'/composer/')) {
|
||||
$this->filesystem->removeDirectory($this->config->get('vendor-dir').'/composer/');
|
||||
}
|
||||
if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir'))) {
|
||||
$this->filesystem->removeDirectory($this->config->get('vendor-dir'));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// clean up
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
if (file_exists($fileName)) {
|
||||
$this->filesystem->unlink($fileName);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -33,9 +35,9 @@ class DownloadManager
|
|||
/**
|
||||
* Initializes download manager.
|
||||
*
|
||||
* @param IOInterface $io The Input Output Interface
|
||||
* @param bool $preferSource prefer downloading from source
|
||||
* @param Filesystem|null $filesystem custom Filesystem object
|
||||
* @param IOInterface $io The Input Output Interface
|
||||
* @param bool $preferSource prefer downloading from source
|
||||
* @param Filesystem|null $filesystem custom Filesystem object
|
||||
*/
|
||||
public function __construct(IOInterface $io, $preferSource = false, Filesystem $filesystem = null)
|
||||
{
|
||||
|
@ -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, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->filesystem->ensureDirectoryExists(dirname($targetDir));
|
||||
|
||||
$sources = $this->getAvailableSources($package, $prevPackage);
|
||||
|
||||
$io = $this->io;
|
||||
$self = $this;
|
||||
|
||||
$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);
|
||||
|
||||
$downloader = $self->getDownloaderForPackage($package);
|
||||
if (!$downloader) {
|
||||
return \React\Promise\resolve();
|
||||
}
|
||||
|
||||
$handleError = function ($e) use ($sources, $source, $package, $io, $download) {
|
||||
if ($e instanceof \RuntimeException) {
|
||||
if (!$sources) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$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 download(PackageInterface $package, $targetDir, $preferSource = null)
|
||||
public function install(PackageInterface $package, $targetDir)
|
||||
{
|
||||
$preferSource = null !== $preferSource ? $preferSource : $this->preferSource;
|
||||
$sourceType = $package->getSourceType();
|
||||
$distType = $package->getDistType();
|
||||
|
||||
$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 (!$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>');
|
||||
}
|
||||
$package->setInstallationSource($source);
|
||||
try {
|
||||
$downloader = $this->getDownloaderForInstalledPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->download($package, $targetDir);
|
||||
}
|
||||
break;
|
||||
} catch (\RuntimeException $e) {
|
||||
if ($i === count($sources) - 1) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->io->writeError(
|
||||
' <warning>Failed to download '.
|
||||
$package->getPrettyName().
|
||||
' from ' . $source . ': '.
|
||||
$e->getMessage().'</warning>'
|
||||
);
|
||||
}
|
||||
$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);
|
||||
}
|
||||
|
||||
if (!count($urls)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
foreach ($urls as $index => $url) {
|
||||
$processedUrl = $this->processUrl($package, $url);
|
||||
$urls[$index] = array(
|
||||
'base' => $url,
|
||||
'processed' => $processedUrl,
|
||||
'cacheKey' => $this->getCacheKey($package, $processedUrl)
|
||||
);
|
||||
}
|
||||
|
||||
if ($output) {
|
||||
$this->io->writeError('');
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function doDownload(PackageInterface $package, $path, $url)
|
||||
{
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
$processedUrl = $this->processUrl($package, $url);
|
||||
$hostname = parse_url($processedUrl, PHP_URL_HOST);
|
||||
$io = $this->io;
|
||||
$cache = $this->cache;
|
||||
$httpDownloader = $this->httpDownloader;
|
||||
$eventDispatcher = $this->eventDispatcher;
|
||||
$filesystem = $this->filesystem;
|
||||
$self = $this;
|
||||
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl);
|
||||
if ($this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
}
|
||||
$rfs = $preFileDownloadEvent->getRemoteFilesystem();
|
||||
$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);
|
||||
}
|
||||
|
||||
try {
|
||||
$checksum = $package->getDistSha1Checksum();
|
||||
$cacheKey = $this->getCacheKey($package, $processedUrl);
|
||||
$cacheKey = $url['cacheKey'];
|
||||
|
||||
// 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);
|
||||
// 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) {
|
||||
$io->writeError(" - Loading <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>) from cache");
|
||||
}
|
||||
$result = \React\Promise\resolve($fileName);
|
||||
} else {
|
||||
$this->io->writeError('Loading from cache', false);
|
||||
if ($output) {
|
||||
$io->writeError(" - Downloading <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
}
|
||||
|
||||
$result = $httpDownloader->addCopy($url['processed'], $fileName, $package->getTransportOptions())
|
||||
->then($accept, $reject);
|
||||
}
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
|
||||
.' directory is writable and you have internet connectivity');
|
||||
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;
|
||||
}
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
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['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);
|
||||
}
|
||||
|
||||
if ($checksum && hash_file('sha1', $fileName) !== $checksum) {
|
||||
throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response->collect();
|
||||
|
||||
return $fileName;
|
||||
};
|
||||
|
||||
$reject = function ($e) use ($io, &$urls, $download, $fileName, $path, $package, &$retries, $filesystem, $self) {
|
||||
// clean up
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->clearLastCacheWrite($package);
|
||||
throw $e;
|
||||
}
|
||||
if (file_exists($fileName)) {
|
||||
$filesystem->unlink($fileName);
|
||||
}
|
||||
$self->clearLastCacheWrite($package);
|
||||
|
||||
return $fileName;
|
||||
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;
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
}
|
||||
|
||||
return $this;
|
||||
$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));
|
||||
}
|
||||
|
||||
protected function clearLastCacheWrite(PackageInterface $package)
|
||||
/**
|
||||
* 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');
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue