1
0
Fork 0

Merge pull request #1 from composer/master

Pull upstream
pull/8955/head
Thomas Lamy 2020-06-05 07:57:20 +02:00 committed by GitHub
commit d4e5135d0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
177 changed files with 4735 additions and 902 deletions

View File

@ -2,7 +2,7 @@ Contributing to Composer
========================
Please note that this project is released with a
[Contributor Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/).
[Contributor Code of Conduct](https://github.com/composer/composer/blob/master/CODE_OF_CONDUCT.md).
By participating in this project you agree to abide by its terms.
Reporting Issues

View File

@ -0,0 +1,158 @@
name: "Continuous Integration"
on:
push:
paths-ignore:
- 'doc/**'
pull_request:
paths-ignore:
- 'doc/**'
env:
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist"
COMPOSER_UPDATE_FLAGS: ""
SYMFONY_PHPUNIT_VERSION: "8.3"
SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1"
jobs:
tests:
name: "CI"
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
php-version:
- "5.3"
- "5.4"
- "5.5"
- "5.6"
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
dependencies: [locked]
os: [ubuntu-latest]
experimental: [false]
include:
- php-version: 5.3
dependencies: highest
os: ubuntu-latest
experimental: false
- php-version: 5.3
dependencies: lowest
os: ubuntu-latest
experimental: false
- php-version: 7.4
dependencies: highest
os: ubuntu-latest
experimental: false
- php-version: 7.4
os: windows-latest
dependencies: locked
experimental: false
- php-version: 7.4
os: macos-latest
dependencies: locked
experimental: false
- php-version: 8.0
dependencies: lowest-ignore
os: ubuntu-latest
experimental: true
- php-version: 8.0
dependencies: highest-ignore
os: ubuntu-latest
experimental: true
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Choose PHPUnit version"
if: "!startsWith(matrix.os, 'windows')"
run: |
if [ "${{ matrix.php-version }}" = "5.3" ] || [ "${{ matrix.php-version }}" = "5.4" ] || [ "${{ matrix.php-version }}" = "5.5" ]; then
echo "::set-env name=SYMFONY_PHPUNIT_VERSION::4.8";
elif [ "${{ matrix.php-version }}" = "5.6" ]; then
echo "::set-env name=SYMFONY_PHPUNIT_VERSION::5.7";
elif [ "${{ matrix.php-version }}" = "7.0" ]; then
echo "::set-env name=SYMFONY_PHPUNIT_VERSION::6.5";
elif [ "${{ matrix.php-version }}" = "7.1" ]; then
echo "::set-env name=SYMFONY_PHPUNIT_VERSION::7.5";
else
echo "::set-env name=SYMFONY_PHPUNIT_VERSION::8.3";
fi
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
extensions: "intl"
ini-values: "memory_limit=-1, phar.readonly=0"
php-version: "${{ matrix.php-version }}"
- name: "Update to latest Composer snapshot"
if: "!startsWith(matrix.os, 'windows')"
run: "sudo -i composer self-update --snapshot"
- name: "Update to latest Composer snapshot on Windows"
if: "startsWith(matrix.os, 'windows')"
run: "composer self-update --snapshot"
- name: "Determine composer cache directory"
id: "determine-composer-cache-directory"
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}"
- name: "Handle lowest dependencies update"
if: "contains(matrix.dependencies, 'lowest')"
run: "echo \"::set-env name=COMPOSER_UPDATE_FLAGS::$COMPOSER_UPDATE_FLAGS --prefer-lowest\""
- name: "Handle ignore-platform-reqs dependencies update"
if: "contains(matrix.dependencies, 'ignore')"
run: "echo \"::set-env name=COMPOSER_FLAGS::$COMPOSER_FLAGS --ignore-platform-req=php\""
- name: "Remove platform config to get latest dependencies for current PHP version when build is not locked"
run: "composer config platform --unset"
- name: "Update dependencies from composer.json using composer binary provided by system"
if: "contains(matrix.dependencies, 'highest') || contains(matrix.dependencies, 'lowest')"
run: "composer update ${{ env.COMPOSER_UPDATE_FLAGS }} ${{ env.COMPOSER_FLAGS }}"
- name: "Install dependencies from composer.lock using composer binary provided by system"
if: "matrix.dependencies == 'locked'"
run: "composer install ${{ env.COMPOSER_FLAGS }}"
- name: "Require latest PHPUnitBridge for PHP 8"
if: "matrix.php-version == '8.0'"
run: |
composer require --no-update --dev symfony/phpunit-bridge:^5.1
composer config -g platform-check false
- name: "Update Symfony's PHPUnitBridge to latest available for the current PHP always as it is not really a dependency of the project"
run: "composer update ${{ env.COMPOSER_FLAGS }} symfony/phpunit-bridge"
- name: "Run install again using composer binary from source"
run: "bin/composer install ${{ env.COMPOSER_FLAGS }}"
- name: "Validate composer.json"
run: "bin/composer validate"
- name: "Prepare git environment"
run: "git config --global user.name composer && git config --global user.email composer@example.com"
- name: "Run tests"
if: "matrix.php-version != '7.3'"
run: "vendor/bin/simple-phpunit"
- name: "Run complete test suite on 7.3"
if: "matrix.php-version == '7.3'"
run: "vendor/bin/simple-phpunit --configuration tests/complete.phpunit.xml"

36
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: "PHP Lint"
on:
push:
paths-ignore:
- 'doc/**'
pull_request:
paths-ignore:
- 'doc/**'
jobs:
tests:
name: "Lint"
runs-on: ubuntu-latest
strategy:
matrix:
php-version:
- "5.3"
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
extensions: "intl"
ini-values: "memory_limit=-1"
php-version: "${{ matrix.php-version }}"
- name: "Lint PHP files"
run: "find src/ -type f -name '*.php' -print0 | xargs -0 -L1 -P4 -- php -l -f"

56
.github/workflows/phpstan.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: "PHPStan"
on:
push:
paths-ignore:
- 'doc/**'
pull_request:
paths-ignore:
- 'doc/**'
env:
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist"
SYMFONY_PHPUNIT_VERSION: ""
jobs:
tests:
name: "PHPStan"
runs-on: ubuntu-latest
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
extensions: "intl"
ini-values: "memory_limit=-1"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Determine composer cache directory"
id: "determine-composer-cache-directory"
run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}"
- name: "Install highest dependencies from composer.json using composer binary provided by system"
run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}"
- name: Run PHPStan
run: |
bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies
vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr

58
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: "Release"
on:
push:
tags:
- "*"
env:
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist"
jobs:
build:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
extensions: "intl"
ini-values: "memory_limit=-1"
php-version: "7.4"
- name: "Install dependencies from composer.lock using composer binary provided by system"
run: "composer install ${{ env.COMPOSER_FLAGS }}"
- name: "Run install again using composer binary from source"
run: "bin/composer install ${{ env.COMPOSER_FLAGS }}"
- name: "Validate composer.json"
run: "bin/composer validate"
- name: Build phar file
run: "php -d phar.readonly=0 bin/compile"
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
body: TODO
- name: Upload phar
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./composer.phar
asset_name: composer.phar
asset_content_type: application/octet-stream

View File

@ -1,93 +0,0 @@
language: php
dist: bionic
git:
depth: 5
cache:
directories:
- $HOME/.composer/cache
matrix:
include:
- php: 5.3
dist: precise
- php: 5.4
dist: trusty
- php: 5.5
dist: trusty
- php: 5.6
dist: xenial
- php: 7.0
dist: xenial
- php: 7.1
dist: xenial
- php: 7.2
dist: xenial
- php: 7.3
dist: xenial
# Regular 7.4 build with locked deps
- php: 7.4
env:
- SYMFONY_PHPUNIT_VERSION=7.5
# High deps check
- php: 7.4
env:
- deps=high
- SYMFONY_PHPUNIT_VERSION=7.5
# PHPStan checks
- php: 7.4
env:
- deps=high
- PHPSTAN=1
- SYMFONY_PHPUNIT_VERSION=7.5
- php: nightly
fast_finish: true
allow_failures:
- php: nightly
before_install:
# 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-progress"
# update deps to latest in case of high deps build
- if [ "$deps" == "high" ]; then composer config platform.php 7.4.0; 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
script:
- if [[ $PHPSTAN == "1" ]]; then
bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --no-update &&
bin/composer update phpstan/* phpunit/* sebastian/* --with-all-dependencies &&
vendor/bin/phpstan analyse --configuration=phpstan/config.neon;
else
vendor/bin/simple-phpunit;
fi
before_deploy:
- php -d phar.readonly=0 bin/compile
deploy:
provider: releases
api_key: $GITHUB_TOKEN
file: composer.phar
skip_cleanup: true
on:
tags: true
repo: composer/composer
php: '7.3'

View File

@ -1,25 +1,54 @@
### [2.0.0-?] 2020-??
### [2.0.0-alpha1] 2020-06-03
* Breaking: This is a major release and while we tried to keep things compatible for most users, you might want to have a look at the [UPGRADE](UPGRADE-2.0.md) guides
* Many CPU and memory performance improvements
* The update command is now much more deterministic as it does not take the already installed packages into account
* Package installation now performs all network operations first before doing any changes on disk, to reduce the chances of ending up with a partially updated vendor dir
* Partial updates and require/remove are now much faster as they only load the metadata required for the updated packages
* Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present
* Added a [platform-check step](doc/07-runtime.md#platform-check) when vendor/autoload.php gets initialized which checks the current PHP version/extensions match what is expected and fails hard otherwise. Can be disabled with the platform-check config option
* Added a [`Composer\InstalledVersions`](doc/07-runtime.md#installed-versions) class which is autoloaded in every project and lets you check which packages/versions are present at runtime
* Added a `composer-runtime-api` virtual package which you can require (as e.g. `^2.0`) to ensure things like the InstalledVersions class above are present. It will effectively force people to use Composer 2.x to install your project
* Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present and we thus strongly recommend enabling curl
* Added much clearer dependency resolution error reporting for common error cases
* Added support for updating to a specific version with partial updates, as well as a [--with flag](doc/03-cli.md#update--u) to pass in temporary constraint overrides
* Added support for TTY mode on Linux/OSX/WSL so that script handlers now run in interactive mode
* Added `only`, `exclude` and `canonical` options to all repositories, see [repository priorities](https://getcomposer.org/repoprio) for details
* Added support for lib-zip platform package
* Added `pre-operations-exec` event to be fired before the packages get installed/upgraded/removed
* Added `pre-pool-create` event to be fired before the package pool for the dependency solver is created, which lets you modify the list of packages going in
* Added `post-file-download` event to be fired after package dist files are downloaded, which lets you do additional checks on the files
* Added --locked flag to `show` command to see the packages from the composer.lock file
* Added --unused flag to `remove` command to make sure any packages which are not needed anymore get removed
* Added --dry-run flag to `require` and `remove` commands
* Added --no-install flag to `update`, `require` and `remove` commands to disable the install step and only do the update step (composer.lock file update)
* Added --with-dependencies and --with-all-dependencies flag aliases to `require` and `remove` commands for consistency with `update`
* Added more info to `vendor/composer/installed.json`, a dev key stores whether dev requirements were installed, and every package now has an install-path key with its install location
* Added COMPOSER_DISABLE_NETWORK which if set makes Composer do its best to run offline. This can be useful when you have poor connectivity or to do benchmarking without network jitter
* Added --json and --merge flags to `config` command to allow editing complex `extra.*` values by using json as input
* Added confirmation prompt when running Composer as superuser in interactive mode
* Added --no-check-version to `validate` command to remove the warning in case the version is defined
* Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements
* Added support for wildcards (`*`) in classmap autoloader paths
* Added support for configuring GitLab deploy tokens in addition to private tokens, see [gitlab-token](doc/06-config.md#gitlab-token)
* Added support for package version guessing for require and init command to take all platform packages into account, not just php version
* Fixed package ordering when autoloading and especially when loading plugins, to make sure dependencies are loaded before their dependents
* Fixed suggest output being very spammy, it now is only one line long and shows more rarely
* Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore
### [1.10.7] 2020-06-03
* Fix PHP 8 deprecations
* Fixed detection of pcntl_signal being in disabled_functions when pcntl_async_signal is allowed
### [1.10.6] 2020-05-06
* Fixed version guessing to take composer-runtime-api and composer-plugin-api requirements into account to avoid selecting packages which require Composer 2
* Fixed package name validation to allow several dashes following each other
* Fixed post-status-cmd script not firing when there were no changes to be displayed
* Fixed composer-runtime-api support on Composer 1.x, the package is now present as 1.0.0
* Fixed support for composer show --name-only --self
* Fixed detection of GitLab URLs when handling authentication in some cases
### [1.10.5] 2020-04-10
* Fixed self-update on PHP <5.6, seriously please upgrade people, it's time
@ -868,6 +897,9 @@
* Initial release
[2.0.0-alpha1]: https://github.com/composer/composer/compare/1.10.7...2.0.0-alpha1
[1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7
[1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6
[1.10.5]: https://github.com/composer/composer/compare/1.10.4...1.10.5
[1.10.4]: https://github.com/composer/composer/compare/1.10.3...1.10.4
[1.10.3]: https://github.com/composer/composer/compare/1.10.2...1.10.3

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at contact@packagist.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -5,7 +5,7 @@ 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)
[![Continuous Integration](https://github.com/composer/composer/workflows/Continuous%20Integration/badge.svg?branch=master)](https://github.com/composer/composer/actions)
Installation / Usage
--------------------

View File

@ -2,11 +2,13 @@
## For composer CLI users
- The new platform-check feature means that Composer checks the runtime PHP version and available extensions to ensure they match the project dependencies. If a mismatch is found, it exits with error details to make sure problems are not overlooked. To avoid issues when deploying to production it is recommended to run `composer check-platform-reqs` with the production PHP process as part of your build or deployment process.
- If a packages exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details.
- Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10
- Package names now must comply to our naming guidelines or Composer will abort, as per the warnings introduced in 1.8.1
- Removed --no-suggest flag as it is not needed anymore
- `update` now lists changes to the lock file first, and then the changes applied when installing the lock file to the vendor dir
- Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1
- Deprecated --no-suggest flag as it is not needed anymore
- PEAR support (repository, downloader, etc.) has been removed
- `update` now lists changes to the lock file first (update step), and then the changes applied when installing the lock file to the vendor dir (install step)
- `HTTPS_PROXY_REQUEST_FULLURI` if not specified will now default to false as this seems to work better in most environments
## For integrators and plugin authors
@ -24,7 +26,9 @@
- packages are now wrapped into a `"packages"` top level key instead of the whole file being the package array
- packages now contain an `"installed-path"` key which lists where they were installed
- there is a top level `"dev"` key which stores whether dev requirements were installed or not
- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overriden by listeners anymore
- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overridden by listeners anymore
- `VersionSelector::findBestCandidate`'s third argument (phpVersion) was removed in favor of passing in a complete PlatformRepository instance into the constructor
- `InitCommand::determineRequirements`'s fourth argument (phpVersion) should now receive a complete PlatformRepository instance or null if platform requirements are to be ignored
- `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods
- `RepositoryInterface` changes:
- A new `loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)` function was added for use during pool building
@ -39,6 +43,7 @@
- All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled, then finally cleanup is called for all. Therefore for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can be undone as much as possible.
- If you used `RemoteFilesystem` you probably should use `HttpDownloader` instead now
- `PRE_DEPENDENCIES_SOLVING` and `POST_DEPENDENCIES_SOLVING` events have been removed, use the new `PRE_OPERATIONS_EXEC` or other existing events instead or talk to us if you think you really need this
- The bundled composer/semver is now the 3.x range, see release notes for [2.0](https://github.com/composer/semver/releases/tag/2.0.0) and [3.0](https://github.com/composer/semver/releases/tag/3.0.0) for the minor breaking changes there
## For Composer repository implementors

View File

@ -1,42 +0,0 @@
build: false
clone_depth: 5
environment:
# This sets the PHP version (from Chocolatey)
PHPCI_CHOCO_VERSION: 7.3.14
PHPCI_CACHE: C:\tools\phpci
PHPCI_PHP: C:\tools\phpci\php
PHPCI_COMPOSER: C:\tools\phpci\composer
cache:
- '%PHPCI_CACHE% -> appveyor.yml'
init:
- 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 %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)
- IF %PHP%==0 cd %PHPCI_PHP%
- IF %PHP%==0 copy php.ini-production php.ini /Y
- IF %PHP%==0 echo date.timezone="UTC" >> php.ini
- IF %PHP%==0 echo extension_dir=ext >> php.ini
- IF %PHP%==0 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==0 echo extension=php_mbstring.dll >> php.ini
- IF %PHP%==0 echo extension=php_fileinfo.dll >> php.ini
- IF %PHP%==0 echo extension=php_intl.dll >> php.ini
- IF %PHP%==0 echo extension=php_curl.dll >> php.ini
- cd %APPVEYOR_BUILD_FOLDER%
- composer install --prefer-dist --no-progress
test_script:
- cd %APPVEYOR_BUILD_FOLDER%
- vendor\bin\simple-phpunit --colors=always

View File

@ -24,24 +24,21 @@
"require": {
"php": "^5.3.2 || ^7.0",
"composer/ca-bundle": "^1.0",
"composer/semver": "^2.0@dev",
"composer/semver": "^3.0",
"composer/spdx-licenses": "^1.2",
"composer/xdebug-handler": "^1.1",
"justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
"justinrainbow/json-schema": "^5.2.10",
"psr/log": "^1.0",
"seld/jsonlint": "^1.4",
"seld/phar-utils": "^1.0",
"symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0",
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0",
"symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0",
"symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
"symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
"react/promise": "^1.2 || ^2.7"
},
"conflict": {
"symfony/console": "2.8.38"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.4",
"symfony/phpunit-bridge": "^4.2 || ^5.0",
"phpspec/prophecy": "^1.10"
},
"suggest": {
@ -52,7 +49,8 @@
"config": {
"platform": {
"php": "5.3.9"
}
},
"platform-check": false
},
"extra": {
"branch-alias": {

151
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a0a9399315ac0b612d4296b8df745112",
"content-hash": "b39e04ca4a44810c96876dae02629707",
"packages": [
{
"name": "composer/ca-bundle",
@ -79,28 +79,29 @@
},
{
"name": "composer/semver",
"version": "2.0.x-dev",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "4df5ff3249f01018504939d66040d8d2b783d820"
"reference": "3426bd5efa8a12d230824536c42a8a4ad30b7940"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/4df5ff3249f01018504939d66040d8d2b783d820",
"reference": "4df5ff3249f01018504939d66040d8d2b783d820",
"url": "https://api.github.com/repos/composer/semver/zipball/3426bd5efa8a12d230824536c42a8a4ad30b7940",
"reference": "3426bd5efa8a12d230824536c42a8a4ad30b7940",
"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"
"phpstan/phpstan": "^0.12.19",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
"dev-master": "3.x-dev"
}
},
"autoload": {
@ -139,19 +140,23 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/2.0"
"source": "https://github.com/composer/semver/tree/3.0.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2020-03-11T13:41:23+00:00"
"time": "2020-05-26T18:22:04+00:00"
},
{
"name": "composer/spdx-licenses",
@ -220,16 +225,16 @@
},
{
"name": "composer/xdebug-handler",
"version": "1.4.1",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
"reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7"
"reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7",
"reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
"reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51",
"shasum": ""
},
"require": {
@ -269,22 +274,30 @@
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2020-03-01T12:26:26+00:00"
"time": "2020-06-04T11:16:35+00:00"
},
{
"name": "justinrainbow/json-schema",
"version": "5.2.9",
"version": "5.2.10",
"source": {
"type": "git",
"url": "https://github.com/justinrainbow/json-schema.git",
"reference": "44c6787311242a979fa15c704327c20e7221a0e4"
"reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4",
"reference": "44c6787311242a979fa15c704327c20e7221a0e4",
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
"reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
"shasum": ""
},
"require": {
@ -339,9 +352,9 @@
],
"support": {
"issues": "https://github.com/justinrainbow/json-schema/issues",
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.9"
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.10"
},
"time": "2019-09-25T14:49:45+00:00"
"time": "2020-05-27T16:41:55+00:00"
},
{
"name": "psr/log",
@ -428,25 +441,35 @@
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/1.0"
},
"time": "2016-03-07T13:46:50+00:00"
},
{
"name": "seld/jsonlint",
"version": "1.7.2",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/jsonlint.git",
"reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19"
"reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19",
"reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1",
"reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7.0"
"php": "^5.3 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
@ -480,9 +503,19 @@
],
"support": {
"issues": "https://github.com/Seldaek/jsonlint/issues",
"source": "https://github.com/Seldaek/jsonlint/tree/1.7.2"
"source": "https://github.com/Seldaek/jsonlint/tree/master"
},
"time": "2019-10-24T14:27:39+00:00"
"funding": [
{
"url": "https://github.com/Seldaek",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
"type": "tidelift"
}
],
"time": "2020-04-30T19:05:18+00:00"
},
{
"name": "seld/phar-utils",
@ -763,16 +796,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.15.0",
"version": "v1.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14"
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
"reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
"shasum": ""
},
"require": {
@ -784,7 +817,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
"dev-master": "1.17-dev"
}
},
"autoload": {
@ -818,7 +851,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.17.0"
},
"funding": [
{
@ -834,20 +867,20 @@
"type": "tidelift"
}
],
"time": "2020-02-27T09:26:54+00:00"
"time": "2020-05-12T16:14:59+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.15.0",
"version": "v1.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
"shasum": ""
},
"require": {
@ -859,7 +892,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
"dev-master": "1.17-dev"
}
},
"autoload": {
@ -894,7 +927,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.17.0"
},
"funding": [
{
@ -910,7 +943,7 @@
"type": "tidelift"
}
],
"time": "2020-03-09T19:04:49+00:00"
"time": "2020-05-12T16:47:27+00:00"
},
{
"name": "symfony/process",
@ -1398,23 +1431,23 @@
},
{
"name": "symfony/phpunit-bridge",
"version": "v3.4.39",
"version": "v4.2.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git",
"reference": "c02893ae43532b46a4f0e0f207d088b939f278d9"
"reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c02893ae43532b46a4f0e0f207d088b939f278d9",
"reference": "c02893ae43532b46a4f0e0f207d088b939f278d9",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7",
"reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0"
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
},
"suggest": {
"symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
@ -1425,7 +1458,7 @@
"type": "symfony-bridge",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
"dev-master": "4.2-dev"
},
"thanks": {
"name": "phpunit/phpunit",
@ -1460,30 +1493,14 @@
"description": "Symfony PHPUnit Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38"
"source": "https://github.com/symfony/phpunit-bridge/tree/4.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-21T08:01:47+00:00"
"time": "2019-07-05T06:33:37+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"composer/semver": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View File

@ -10,7 +10,7 @@ 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
@ -58,7 +58,7 @@ project, or globally as a system wide executable.
#### Locally
To install Composer locally, run the installer in your project directory. See
To install Composer locally, run the installer in your project directory. See
[the Download page](https://getcomposer.org/download/) for instructions.
The installer will check a few PHP settings and then download `composer.phar`
@ -134,8 +134,16 @@ to download `composer.phar`.
Create a new `composer.bat` file alongside `composer.phar`:
Using cmd.exe:
```sh
C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat
C:\bin> echo @php "%~dp0composer.phar" %*>composer.bat
```
Using PowerShell:
```sh
PS C:\bin> Set-Content composer.bat '@php "%~dp0composer.phar" %*'
```
Add the directory to your PATH environment variable if it isn't already.

View File

@ -112,9 +112,13 @@ resolution.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`,
`lib-*` and `ext-*`) and force the installation even if the local machine does
not fulfill these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it.
## update / u
@ -142,6 +146,26 @@ You can also use wildcards to update a bunch of packages at once:
php composer.phar update "vendor/*"
```
If you want to downgrade a package to a specific version without changing your
composer.json you can use `--with` and provide a custom version constraint:
```sh
php composer.phar update --with vendor/package:2.0.1
```
The custom constraint has to be a subset of the existing constraint you have,
and this feature is only available for your root package dependencies.
If you only want to update the package(s) for which you provide custom constraints
using `--with`, you can skip `--with` and just use constraints with the partial
update syntax:
```sh
php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.*
```
### Options
* **--prefer-source:** Install packages from `source` when available.
@ -149,8 +173,10 @@ php composer.phar update "vendor/*"
* **--dry-run:** Simulate the command without actually doing anything.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
* **--no-install:** Does not run the install step after updating the composer.lock file.
* **--lock:** Only updates the lock file hash to suppress warning about the
lock file being out of date.
* **--with:** Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0
* **--no-autoloader:** Skips autoloader generation.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-progress:** Removes the progress display that can mess with some
@ -163,15 +189,21 @@ php composer.phar update "vendor/*"
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`,
`lib-*` and `ext-*`) and force the installation even if the local machine does
not fulfill these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it.
* **--prefer-stable:** Prefer stable versions of dependencies.
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
versions of requirements, generally used with `--prefer-stable`.
* **--interactive:** Interactive interface with autocompletion to select the packages to update.
* **--root-reqs:** Restricts the update to your first degree dependencies.
Specifying one of the words `mirrors`, `lock`, or `nothing` as an argument has the same effect as specifying the option `--lock`, for example `composer update mirrors` is exactly the same as `composer update --lock`.
## require
The `require` command adds new packages to the `composer.json` file from
@ -201,14 +233,19 @@ If you do not specify a package, composer will prompt you to search for a packag
* **--prefer-dist:** Install packages from `dist` when available.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-update:** Disables the automatic update of the dependencies (implies --no-install).
* **--no-install:** Does not run the install step after updating the composer.lock file.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
* **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements.
* **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`,
`lib-*` and `ext-*`) and force the installation even if the local machine does
not fulfill these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it.
* **--prefer-stable:** Prefer stable versions of dependencies.
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
versions of requirements, generally used with `--prefer-stable`.
@ -237,13 +274,18 @@ uninstalled.
* **--dry-run:** Simulate the command without actually doing anything.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-update:** Disables the automatic update of the dependencies (implies --no-install).
* **--no-install:** Does not run the install step after updating the composer.lock file.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--update-no-dev:** Run the dependency update with the --no-dev option.
* **--update-with-dependencies:** Also update dependencies of the removed packages.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`,
`lib-*` and `ext-*`) and force the installation even if the local machine does
not fulfill these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it.
* **--optimize-autoloader (-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.
@ -357,6 +399,7 @@ php composer.phar show monolog/monolog 1.0.2
* **--all :** List all packages available in all your repositories.
* **--installed (-i):** List the packages that are installed (this is enabled by default, and deprecated).
* **--locked:** List the locked packages from composer.lock.
* **--platform (-p):** List only platform packages (php & extensions).
* **--available (-a):** List available packages only.
* **--self (-s):** List the root package info.
@ -605,6 +648,8 @@ See the [Config](06-config.md) chapter for valid configuration options.
that this cannot be used in conjunction with the `--global` option.
* **--absolute:** Returns absolute paths when fetching *-dir config values
instead of relative.
* **--json:** JSON decode the setting value, to be used with `extra.*` keys.
* **--merge:** Merge the setting value with the current value, to be used with `extra.*` keys in combination with `--json`.
### Modifying Repositories
@ -633,6 +678,13 @@ php composer.phar config extra.foo.bar value
The dots indicate array nesting, a max depth of 3 levels is allowed though. The above
would set `"extra": { "foo": { "bar": "value" } }`.
If you have a complex value to add/modify, you can use the `--json` and `--merge` flags
to edit extra fields as json:
```sh
php composer.phar config --json extra.foo.bar '{"baz": true, "qux": []}'
```
## create-project
You can use Composer to create new projects from an existing package. This is
@ -686,9 +738,13 @@ By default the command checks for the packages on packagist.org.
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
fulfill these.
* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`,
`lib-*` and `ext-*`) and force the installation even if the local machine does
not fulfill these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it.
## dump-autoload (dumpautoload)
@ -712,6 +768,11 @@ performance.
Implicitly enables `--optimize`.
* **--apcu:** Use APCu to cache found/not-found classes.
* **--no-dev:** Disables autoload-dev rules.
* **--ignore-platform-reqs:** ignore all `php`, `hhvm`, `lib-*` and `ext-*`
requirements and skip the [platform check](07-runtime.md#platform-check) for these.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement (`php`, `hhvm`,
`lib-*` and `ext-*`) and skip the [platform check](07-runtime.md#platform-check) for it.
## clear-cache / clearcache / cc

View File

@ -1,4 +1,4 @@
# The composer.json Schema
# The composer.json schema
This chapter will explain all of the fields available in `composer.json`.
@ -34,12 +34,12 @@ separated by `/`. Examples:
* monolog/monolog
* igorw/event-source
The name can contain any character, including white spaces, and it's case
insensitive (`foo/bar` and `Foo/Bar` are considered the same package). In order
to simplify its installation, it's recommended to define a short and lowercase
name that doesn't include non-alphanumeric characters or white spaces.
The name must be lowercased and consist of words separated by `-`, `.` or `_`.
The complete name should match `^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$`.
Required for published packages (libraries).
The `name` property is required for published packages (libraries).
> **Note:** Before Composer version 2.0, a name could contain any character, including white spaces.
### description
@ -610,6 +610,18 @@ Example:
}
```
Wildcards (`*`) are also supported in a classmap paths, and expand to match any directory name:
Example:
```json
{
"autoload": {
"classmap": ["src/addons/*/lib/", "3rd-party/*", "Something.php"]
}
}
```
#### Files
If you want to require certain files explicitly on every request then you can use
@ -877,6 +889,21 @@ A set of options for creating package archives.
The following options are supported:
* **name:** Allows configuring base name for archive.
By default (if not configured, and `--file` is not passed as command-line argument),
`preg_replace('#[^a-z0-9-_]#i', '-', name)` is used.
Example:
```json
{
"name": "org/strangeName",
"archive": {
"name": "Strange_name"
}
}
```
* **exclude:** Allows configuring a list of patterns for excluded paths. The
pattern syntax matches .gitignore files. A leading exclamation mark (!) will
result in any matching files to be included even if a previous pattern

View File

@ -85,9 +85,13 @@ gitlab.com the domain names must be also specified with the
## gitlab-token
A list of domain names and private tokens. For example using `{"gitlab.com":
A list of domain names and private tokens. Private token can be either simple
string, or array with username and token. For example using `{"gitlab.com":
"privatetoken"}` as the value of this option will use `privatetoken` to access
private repositories on gitlab. Please note: If the package is not hosted at
private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser",
"token": "privatetoken"}}` will use both username and token for gitlab deploy
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
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.
@ -307,4 +311,9 @@ in the composer home, cache, and data directories.
Defaults to `true`. If set to `false`, Composer will not create a `composer.lock`
file.
&larr; [Repositories](05-repositories.md) | [Community](07-community.md) &rarr;
## platform-check
Defaults to `true`. If set to `false`, Composer will not create and require a
`platform_check.php` file as part of the autoloader bootstrap.
&larr; [Repositories](05-repositories.md) | [Runtime](07-runtime.md) &rarr;

109
doc/07-runtime.md Normal file
View File

@ -0,0 +1,109 @@
# Runtime Composer utilities
While Composer is mostly used around your project to install its dependencies,
there are a few things which are made available to you at runtime.
If you need to rely on some of these in a specific version, you can require
the `composer-runtime-api` package.
## Autoload
The autoloader is the most used one, and is already covered in our
[basic usage guide](#01-basic-usage.md#autoloading). It is available in all
Composer versions.
## Installed versions
composer-runtime-api 2.0 introduced a new `Composer\InstalledVersions` class which offers
a few static methods to inspect which versions are currently installed. This is
automatically available to your code as long as you include the Composer autoloader.
The main use cases for this class are the following:
### Knowing whether package X (or virtual package) is present
```php
\Composer\InstalledVersions::isInstalled('vendor/package'); // returns bool
\Composer\InstalledVersions::isInstalled('psr/log-implementation'); // returns bool
```
Note that this can not be used to check whether platform packages are installed.
### Knowing whether package X is installed in version Y
> **Note:** To use this, your package must require `"composer/semver": "^2.0"`.
```php
use Composer\Semver\VersionParser;
\Composer\InstalledVersions::satisfies(new VersionParser, 'vendor/package', '2.0.*');
\Composer\InstalledVersions::satisfies(new VersionParser, 'psr/log-implementation', '^1.0');
```
This will return true if e.g. vendor/package is installed in a version matching
`2.0.*`, but also if the given package name is replaced or provided by some other
package.
### Knowing the version of package X
> **Note:** This will return `null` if the package name you ask for is not itself installed
> but merely provided or replaced by another package. We therefore recommend using satisfies()
> in library code at least. In application code you have a bit more control and it is less
> important.
```php
// returns a normalized version (e.g. 1.2.3.0) if vendor/package is installed,
// or null if it is provided/replaced,
// or throws OutOfBoundsException if the package is not installed at all
\Composer\InstalledVersions::getVersion('vendor/package');
```
```php
// returns the original version (e.g. v1.2.3) if vendor/package is installed,
// or null if it is provided/replaced,
// or throws OutOfBoundsException if the package is not installed at all
\Composer\InstalledVersions::getPrettyVersion('vendor/package');
```
```php
// returns the package dist or source reference (e.g. a git commit hash) if vendor/package is installed,
// or null if it is provided/replaced,
// or throws OutOfBoundsException if the package is not installed at all
\Composer\InstalledVersions::getReference('vendor/package');
```
### Knowing a package's own installed version
If you are only interested in getting a package's own version, e.g. in the source of acme/foo you want
to know which version acme/foo is currently running to display that to the user, then it is
acceptable to use getVersion/getPrettyVersion/getReference.
The warning in the section above does not apply in this case as you are sure the package is present
and not being replaced if your code is running.
It is nonetheless a good idea to make sure you handle the `null` return value as gracefully as
possible for safety.
----
A few other methods are available for more complex usages, please refer to the
source/docblocks of the class itself.
## Platform check
composer-runtime-api 2.0 introduced a new `vendor/composer/platform_check.php` file, which
is included automatically when you include the Composer autoloader.
It verifies that platform requirements (i.e. php and php extensions) are fulfilled
by the PHP process currently running. If the requirements are not met, the script
prints a warning with the missing requirements and exits with code 104.
To avoid an unexpected white page of death with some obscure PHP extension warning in
production, you can run `composer check-platform-reqs --no-dev` as part of your
deployment/build and if that returns a non-0 code you should abort.
If you for some reason do not want to use this safety check, and would rather
risk runtime errors when your code executes, you can disable this by setting the
[`platform-check`](06-config.md#platform-check) config option to `false`.
&larr; [Config](06-config.md) | [Community](08-community.md) &rarr;

View File

@ -32,4 +32,4 @@ for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for developme
Stack Overflow has a growing collection of
[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
&larr; [Config](06-config.md)
&larr; [Config](07-runtime.md)

View File

@ -2,7 +2,7 @@
tagline: How to reduce the performance impact of the autoloader
-->
# Autoloader Optimization
# Autoloader optimization
By default, the Composer autoloader runs relatively fast. However, due to the way
PSR-4 and PSR-0 autoloading rules are set up, it needs to check the filesystem

View File

@ -375,4 +375,7 @@ You can set custom script descriptions with the following in your `composer.json
}
```
The descriptions are used in `composer list` or `composer run -l` commands to
describe what the scripts do when the command is run.
> **Note:** You can only set custom descriptions of custom commands.

View File

@ -1,5 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../src/bootstrap.php';

View File

@ -1,7 +1,7 @@
parameters:
level: 1
autoload_files:
- autoload.php
- '../src/bootstrap.php'
excludes_analyse:
- '../tests/Composer/Test/Fixtures/*'
- '../tests/Composer/Test/Autoload/Fixtures/*'
@ -22,9 +22,6 @@ parameters:
# variable defined in eval
- '~^Undefined variable: \$res$~'
# erroneous detection of missing const, see https://github.com/phpstan/phpstan/issues/2960
- '~^Access to undefined constant ZipArchive::LIBZIP_VERSION.$~'
# we don't have different constructors for parent/child
- '~^Unsafe usage of new static\(\)\.$~'

View File

@ -10,7 +10,6 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
failOnRisky="true"
strict="false"
processIsolation="false"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"

View File

@ -7,7 +7,8 @@
"properties": {
"name": {
"type": "string",
"description": "Package name, including 'vendor-name/' prefix."
"description": "Package name, including 'vendor-name/' prefix.",
"pattern": "^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$"
},
"type": {
"description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.",
@ -305,6 +306,10 @@
"lock": {
"type": "boolean",
"description": "Defaults to true. If set to false, Composer will not create a composer.lock file."
},
"platform-check": {
"type": "boolean",
"description": "Defaults to true. If set to false, Composer will not create and require a platform_check.php file as part of the autoloader bootstrap."
}
}
},
@ -354,6 +359,10 @@
"type": ["object"],
"description": "Options for creating package archives for distribution.",
"properties": {
"name": {
"type": "string",
"description": "A base name for archive."
},
"exclude": {
"type": "array",
"description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark."

View File

@ -19,6 +19,9 @@ use Composer\IO\IOInterface;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\PlatformRepository;
use Composer\Semver\Constraint\Bound;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Util\Filesystem;
use Composer\Script\ScriptEvents;
use Composer\Util\PackageSorter;
@ -59,6 +62,11 @@ class AutoloadGenerator
*/
private $runScripts = false;
/**
* @var bool|array
*/
private $ignorePlatformReqs = false;
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
{
$this->eventDispatcher = $eventDispatcher;
@ -101,6 +109,26 @@ class AutoloadGenerator
$this->runScripts = (bool) $runScripts;
}
/**
* Sets whether platform requirements should be ignored
*
* If this is set to true, the platform check file will not be generated
* If this is set to false, the platform check file will be generated with all requirements
* If this is set to string[], those packages will be ignored from the platform check file
*
* @param array|bool $ignorePlatformReqs
*/
public function setIgnorePlatformRequirements($ignorePlatformReqs)
{
if (is_array($ignorePlatformReqs)) {
$this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) {
return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req);
});
} else {
$this->ignorePlatformReqs = (bool) $ignorePlatformReqs;
}
}
public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '')
{
if ($this->classMapAuthoritative) {
@ -276,6 +304,7 @@ EOF;
);
}
$classMap['Composer\\InstalledVersions'] = "\$vendorDir . '/composer/InstalledVersions.php',\n";
ksort($classMap);
foreach ($classMap as $class => $code) {
$classmapFile .= ' '.var_export($class, true).' => '.$code;
@ -295,27 +324,40 @@ EOF;
}
}
$this->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile);
$this->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File);
$this->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile);
$filesystem->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile);
$filesystem->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File);
$filesystem->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile);
$includePathFilePath = $targetDir.'/include_paths.php';
if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
$this->filePutContentsIfModified($includePathFilePath, $includePathFileContents);
$filesystem->filePutContentsIfModified($includePathFilePath, $includePathFileContents);
} elseif (file_exists($includePathFilePath)) {
unlink($includePathFilePath);
}
$includeFilesFilePath = $targetDir.'/autoload_files.php';
if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
$this->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents);
$filesystem->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents);
} elseif (file_exists($includeFilesFilePath)) {
unlink($includeFilesFilePath);
}
$this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion));
$this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
$this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion));
$filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion));
$checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true;
$platformCheckContent = null;
if ($checkPlatform) {
$platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array());
if (null === $platformCheckContent) {
$checkPlatform = false;
}
}
if ($checkPlatform) {
$filesystem->filePutContentsIfModified($targetDir.'/platform_check.php', $platformCheckContent);
} elseif (file_exists($targetDir.'/platform_check.php')) {
unlink($targetDir.'/platform_check.php');
}
$filesystem->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
$filesystem->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform));
$this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
$this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
$filesystem->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
$filesystem->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
if ($this->runScripts) {
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
@ -326,16 +368,6 @@ EOF;
return count($classMap);
}
private function filePutContentsIfModified($path, $content)
{
$currentContent = @file_get_contents($path);
if (!$currentContent || ($currentContent != $content)) {
return file_put_contents($path, $content);
}
return 0;
}
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles)
{
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) {
@ -570,6 +602,135 @@ EOF;
return $baseDir . (($path !== false) ? var_export($path, true) : "");
}
protected function getPlatformCheck($packageMap, array $ignorePlatformReqs)
{
$lowestPhpVersion = Bound::zero();
$requiredExtensions = array();
$extensionProviders = array();
foreach ($packageMap as $item) {
list($package, $installPath) = $item;
foreach (array_merge($package->getReplaces(), $package->getProvides()) as $link) {
if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) {
$extensionProviders[$match[1]][] = $link->getConstraint() ?: new MatchAllConstraint();
}
}
}
foreach ($packageMap as $item) {
list($package, $installPath) = $item;
foreach ($package->getRequires() as $link) {
if (in_array($link->getTarget(), $ignorePlatformReqs, true)) {
continue;
}
if ('php' === $link->getTarget() && ($constraint = $link->getConstraint())) {
if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) {
$lowestPhpVersion = $constraint->getLowerBound();
}
}
if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) {
// skip extension checks if they have a valid provider/replacer
if (isset($extensionProviders[$match[1]])) {
foreach ($extensionProviders[$match[1]] as $provided) {
if (!$link->getConstraint() || $provided->matches($link->getConstraint())) {
continue 2;
}
}
}
$extension = var_export($match[1], true);
if ($match[1] === 'pcntl' || $match[1] === 'readline') {
$requiredExtensions[$extension] = "PHP_SAPI !== 'cli' || extension_loaded($extension) || \$missingExtensions[] = $extension;\n";
} else {
$requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n";
}
}
}
}
ksort($requiredExtensions);
$formatToPhpVersionId = function (Bound $bound) {
if ($bound->isZero()) {
return 0;
}
if ($bound->isPositiveInfinity()) {
return 99999;
}
$version = str_replace('-', '.', $bound->getVersion());
$chunks = array_map('intval', explode('.', $version));
return $chunks[0] * 10000 + $chunks[1] * 100 + $chunks[2];
};
$formatToHumanReadable = function (Bound $bound) {
if ($bound->isZero()) {
return 0;
}
if ($bound->isPositiveInfinity()) {
return 99999;
}
$version = str_replace('-', '.', $bound->getVersion());
$chunks = explode('.', $version);
$chunks = array_slice($chunks, 0, 3);
return implode('.', $chunks);
};
$requiredPhp = '';
$requiredPhpError = '';
if (!$lowestPhpVersion->isZero()) {
$operator = $lowestPhpVersion->isInclusive() ? '>=' : '>';
$requiredPhp = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($lowestPhpVersion);
$requiredPhpError = '"'.$operator.' '.$formatToHumanReadable($lowestPhpVersion).'"';
}
if ($requiredPhp) {
$requiredPhp = <<<PHP_CHECK
if (!($requiredPhp)) {
\$issues[] = 'Your Composer dependencies require a PHP version $requiredPhpError. You are running ' . PHP_VERSION . '.';
}
PHP_CHECK;
}
$requiredExtensions = implode('', $requiredExtensions);
if ('' !== $requiredExtensions) {
$requiredExtensions = <<<EXT_CHECKS
\$missingExtensions = array();
$requiredExtensions
if (\$missingExtensions) {
\$issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', \$missingExtensions);
}
EXT_CHECKS;
}
if (!$requiredPhp && !$requiredExtensions) {
return null;
}
return <<<PLATFORM_CHECK
<?php
// platform_check.php @generated by Composer
\$issues = array();
${requiredPhp}${requiredExtensions}
if (\$issues) {
echo 'Composer detected issues in your platform:' . "\\n\\n" . implode("\\n", \$issues);
exit(104);
}
PLATFORM_CHECK;
}
protected function getAutoloadFile($vendorPathToTargetDirCode, $suffix)
{
$lastChar = $vendorPathToTargetDirCode[strlen($vendorPathToTargetDirCode) - 1];
@ -591,7 +752,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
AUTOLOAD;
}
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion = 70000)
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform)
{
$file = <<<HEADER
<?php
@ -618,12 +779,24 @@ class ComposerAutoloaderInit$suffix
return self::\$loader;
}
HEADER;
if ($checkPlatform) {
$file .= <<<'PLATFORM_CHECK'
require __DIR__ . '/platform_check.php';
PLATFORM_CHECK;
}
$file .= <<<CLASSLOADER_INIT
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'), true, $prependAutoloader);
self::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));
HEADER;
CLASSLOADER_INIT;
if ($useIncludePath) {
$file .= <<<'INCLUDE_PATH'
@ -638,7 +811,7 @@ INCLUDE_PATH;
$file .= <<<STATIC_INIT
\$useStaticLoader = PHP_VERSION_ID >= $staticPhpVersion && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if (\$useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit$suffix::getInitializer(\$loader));
} else {
@ -1020,51 +1193,4 @@ INITIALIZER;
return $sortedPackageMap;
}
/**
* Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463
*
* @param string $source
* @param string $target
*/
protected function safeCopy($source, $target)
{
if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) {
$source = fopen($source, 'r');
$target = fopen($target, 'w+');
stream_copy_to_stream($source, $target);
fclose($source);
fclose($target);
}
}
/**
* compare 2 files
* https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files
*/
private function filesAreEqual($a, $b)
{
// Check if filesize is different
if (filesize($a) !== filesize($b)) {
return false;
}
// Check if content is different
$ah = fopen($a, 'rb');
$bh = fopen($b, 'rb');
$result = true;
while (!feof($ah)) {
if (fread($ah, 8192) != fread($bh, 8192)) {
$result = false;
break;
}
}
fclose($ah);
fclose($bh);
return $result;
}
}

View File

@ -65,7 +65,7 @@ class ClassMapGenerator
if (is_string($path)) {
if (is_file($path)) {
$path = array(new \SplFileInfo($path));
} elseif (is_dir($path)) {
} elseif (is_dir($path) || strpos($path, '*') !== false) {
$path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path);
} else {
throw new \RuntimeException(

View File

@ -19,6 +19,7 @@ use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\IO\NullIO;
use Composer\Plugin\PreCommandRunEvent;
use Composer\Package\Version\VersionParser;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -180,4 +181,25 @@ abstract class BaseCommand extends Command
return array($preferSource, $preferDist);
}
protected function formatRequirements(array $requirements)
{
$requires = array();
$requirements = $this->normalizeRequirements($requirements);
foreach ($requirements as $requirement) {
if (!isset($requirement['version'])) {
throw new \UnexpectedValueException('Option '.$requirement['name'] .' is missing a version constraint, use e.g. '.$requirement['name'].':^1.0');
}
$requires[$requirement['name']] = $requirement['version'];
}
return $requires;
}
protected function normalizeRequirements(array $requirements)
{
$parser = new VersionParser();
return $parser->parseNameVersionPairs($requirements);
}
}

View File

@ -20,6 +20,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RootPackageRepository;
use Composer\Repository\InstalledRepository;
class CheckPlatformReqsCommand extends BaseCommand
@ -47,7 +48,7 @@ EOT
{
$composer = $this->getComposer();
$requires = $composer->getPackage()->getRequires();
$requires = array();
if ($input->getOption('no-dev')) {
$installedRepo = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'));
$dependencies = $installedRepo->getPackages();
@ -63,7 +64,7 @@ EOT
$requires[$require] = array($link);
}
$installedRepo = new InstalledRepository(array($installedRepo));
$installedRepo = new InstalledRepository(array($installedRepo, new RootPackageRepository($composer->getPackage())));
foreach ($installedRepo->getPackages() as $package) {
foreach ($package->getRequires() as $require => $link) {
$requires[$require][] = $link;

View File

@ -73,6 +73,8 @@ class ConfigCommand extends BaseCommand
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json'),
new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
new InputOption('json', 'j', InputOption::VALUE_NONE, 'JSON decode the setting value, to be used with extra.* keys'),
new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'),
new InputArgument('setting-key', null, 'Setting key'),
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
))
@ -119,6 +121,10 @@ To add or edit extra properties you can use:
<comment>%command.full_name% extra.property value</comment>
Or to add a complex value you can use json with:
<comment>%command.full_name% extra.property --json '{"foo":true, "bar": []}'</comment>
To edit the file in an external editor:
<comment>%command.full_name% --editor</comment>
@ -421,6 +427,7 @@ EOT
'github-expose-hostname' => array($booleanValidator, $booleanNormalizer),
'htaccess-protect' => array($booleanValidator, $booleanNormalizer),
'lock' => array($booleanValidator, $booleanNormalizer),
'platform-check' => array($booleanValidator, $booleanNormalizer),
);
$multiConfigValues = array(
'github-protocols' => array(
@ -621,7 +628,21 @@ EOT
return 0;
}
$this->configSource->addProperty($settingKey, $values[0]);
$value = $values[0];
if ($input->getOption('json')) {
$value = JsonFile::parseJson($value);
if ($input->getOption('merge')) {
$currentValue = $this->configFile->read();
$bits = explode('.', $settingKey);
foreach ($bits as $bit) {
$currentValue = isset($currentValue[$bit]) ? $currentValue[$bit] : null;
}
if (is_array($currentValue)) {
$value = array_merge($currentValue, $value);
}
}
}
$this->configSource->addProperty($settingKey, $value);
return 0;
}

View File

@ -80,7 +80,8 @@ class CreateProjectCommand extends BaseCommand
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).'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'),
))
->setHelp(
<<<EOT
@ -127,6 +128,8 @@ EOT
$input->setOption('no-plugins', true);
}
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
return $this->installProject(
$io,
$config,
@ -143,7 +146,7 @@ EOT
$input->getOption('no-scripts'),
$input->getOption('no-progress'),
$input->getOption('no-install'),
$input->getOption('ignore-platform-reqs'),
$ignorePlatformReqs,
!$input->getOption('no-secure-http'),
$input->getOption('add-repository')
);
@ -336,32 +339,24 @@ EOT
$repositorySet = new RepositorySet($stability);
$repositorySet->addRepository($sourceRepo);
$phpVersion = null;
$prettyPhpVersion = null;
if (!$ignorePlatformReqs) {
$platformOverrides = $config->get('platform') ?: array();
// initialize $this->repos as it is used by the parent InitCommand
$platform = new PlatformRepository(array(), $platformOverrides);
$phpPackage = $platform->findPackage('php', '*');
$phpVersion = $phpPackage->getVersion();
$prettyPhpVersion = $phpPackage->getPrettyVersion();
}
$platformOverrides = $config->get('platform') ?: array();
$platformRepo = new PlatformRepository(array(), $platformOverrides);
// find the latest version if there are multiple
$versionSelector = new VersionSelector($repositorySet);
$package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability);
$versionSelector = new VersionSelector($repositorySet, $platformRepo);
$package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $ignorePlatformReqs);
if (!$package) {
$errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability");
if ($phpVersion && $versionSelector->findBestCandidate($name, $packageVersion, null, $stability)) {
throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version '.$prettyPhpVersion.'.');
if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) {
throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.');
}
throw new \InvalidArgumentException($errorMessage .'.');
}
// handler Ctrl+C for unix-like systems
if (function_exists('pcntl_async_signals')) {
if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
@mkdir($directory, 0777, true);
if ($realDir = realpath($directory)) {
pcntl_async_signals(true);
@ -372,6 +367,22 @@ EOT
});
}
}
// handler Ctrl+C for Windows on PHP 7.4+
if (function_exists('sapi_windows_set_ctrl_handler')) {
@mkdir($directory, 0777, true);
if ($realDir = realpath($directory)) {
sapi_windows_set_ctrl_handler(function () use ($realDir) {
$fs = new Filesystem();
$fs->removeDirectory($realDir);
exit(130);
}, true);
}
}
// avoid displaying 9999999-dev as version if dev-master was selected
if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEV_MASTER_ALIAS) {
$package = $package->getAliasOf();
}
$io->writeError('<info>Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')</info>');

View File

@ -35,6 +35,8 @@ class DumpAutoloadCommand extends BaseCommand
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'),
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.'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'),
))
->setHelp(
<<<EOT
@ -70,11 +72,14 @@ EOT
$this->getIO()->write('<info>Generating autoload files</info>');
}
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
$generator = $composer->getAutoloadGenerator();
$generator->setDevMode(!$input->getOption('no-dev'));
$generator->setClassMapAuthoritative($authoritative);
$generator->setApcu($apcu);
$generator->setRunScripts(!$input->getOption('no-scripts'));
$generator->setIgnorePlatformRequirements($ignorePlatformReqs);
$numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
if ($authoritative) {

View File

@ -99,7 +99,7 @@ EOT
try {
chdir($this->getApplication()->getInitialWorkingDirectory());
} catch (\Exception $e) {
throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getWorkingDirectory().'"', 0, $e);
throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getInitialWorkingDirectory().'"', 0, $e);
}
}

View File

@ -345,13 +345,21 @@ EOT
// prepare to resolve dependencies
$repos = $this->getRepos();
$preferredStability = $minimumStability ?: 'stable';
$phpVersion = $repos->findPackage('php', '*')->getPrettyVersion();
$platformRepo = null;
if ($repos instanceof CompositeRepository) {
foreach ($repos->getRepositories() as $candidateRepo) {
if ($candidateRepo instanceof PlatformRepository) {
$platformRepo = $candidateRepo;
break;
}
}
}
$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, $phpVersion, $preferredStability);
$requirements = $this->determineRequirements($input, $output, $require, $platformRepo, $preferredStability);
}
$input->setOption('require', $requirements);
@ -359,7 +367,7 @@ EOT
$requireDev = $input->getOption('require-dev');
$devRequirements = array();
if ($requireDev || $io->askConfirmation($question, true)) {
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $phpVersion, $preferredStability);
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability);
}
$input->setOption('require-dev', $devRequirements);
}
@ -403,7 +411,7 @@ EOT
return $this->repos;
}
final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false)
final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), PlatformRepository $platformRepo = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false)
{
if ($requires) {
$requires = $this->normalizeRequirements($requires);
@ -413,7 +421,7 @@ EOT
foreach ($requires as $requirement) {
if (!isset($requirement['version'])) {
// determine the best version automatically
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, null, null, $fixed);
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, null, null, $fixed);
$requirement['version'] = $version;
// replace package name from packagist.org
@ -426,7 +434,7 @@ EOT
));
} else {
// check that the specified version/constraint exists before we proceed
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed);
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed);
// replace package name from packagist.org
$requirement['name'] = $name;
@ -550,7 +558,7 @@ EOT
);
if (false === $constraint) {
list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $phpVersion, $preferredStability);
list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $platformRepo, $preferredStability);
$io->writeError(sprintf(
'Using version <info>%s</info> for <info>%s</info>',
@ -577,17 +585,6 @@ EOT
return array($this->parseAuthorString($author));
}
protected function formatRequirements(array $requirements)
{
$requires = array();
$requirements = $this->normalizeRequirements($requirements);
foreach ($requirements as $requirement) {
$requires[$requirement['name']] = $requirement['version'];
}
return $requires;
}
protected function getGitConfig()
{
if (null !== $this->gitConfig) {
@ -652,13 +649,6 @@ EOT
return false;
}
protected function normalizeRequirements(array $requirements)
{
$parser = new VersionParser();
return $parser->parseNameVersionPairs($requirements);
}
protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/')
{
$contents = "";
@ -723,7 +713,7 @@ EOT
*
* @param InputInterface $input
* @param string $name
* @param string|null $phpVersion
* @param PlatformRepository|null $platformRepo
* @param string $preferredStability
* @param string|null $requiredVersion
* @param string $minimumStability
@ -731,49 +721,47 @@ EOT
* @throws \InvalidArgumentException
* @return array name version
*/
private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null)
private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null)
{
// 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;
// handle ignore-platform-reqs flag if present
$ignorePlatformReqs = false;
if ($input->hasOption('ignore-platform-reqs') && $input->hasOption('ignore-platform-req')) {
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
}
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
// find the latest version allowed in this repo set
$versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo);
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs);
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)) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($name, $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)) {
if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) {
throw new \InvalidArgumentException(sprintf(
'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)',
'Package %s at version %s has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version',
$name,
$requiredVersion,
$phpVersion
$requiredVersion
));
}
// Check whether the required version was the problem
if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $phpVersion, $preferredStability)) {
if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability, $ignorePlatformReqs)) {
throw new \InvalidArgumentException(sprintf(
'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)) {
// Check whether the PHP version was the problem for all versions
if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) {
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, PHP extensions and Composer version',
$name
));
}

View File

@ -44,11 +44,13 @@ class InstallCommand extends BaseCommand
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Do not use, only defined here to catch misuse of the install command.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
new InputOption('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.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all 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(
@ -82,6 +84,12 @@ EOT
return 1;
}
if ($input->getOption('no-install')) {
$io->writeError('<error>Invalid option "--no-install". Use "composer update --no-install" instead if you are trying to update the composer.lock file.</error>');
return 1;
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
@ -96,6 +104,8 @@ EOT
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader');
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
@ -107,7 +117,7 @@ EOT
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setIgnorePlatformRequirements($ignorePlatformReqs)
;
if ($input->getOption('no-plugins')) {

View File

@ -41,7 +41,8 @@ class RemoveCommand extends BaseCommand
new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'),
@ -49,7 +50,8 @@ class RemoveCommand extends BaseCommand
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'),
new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'),
new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
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.'),
@ -190,6 +192,8 @@ EOT
}
}
$io->writeError('<info>'.$file.' has been updated</info>');
if ($input->getOption('no-update')) {
return 0;
}
@ -224,12 +228,19 @@ EOT
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
$flags = '';
if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) {
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
$flags .= ' --with-all-dependencies';
} elseif ($input->getOption('no-update-with-dependencies')) {
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
$flags .= ' --with-dependencies';
}
$io->writeError('<info>Running composer update '.implode(' ', $packages).$flags);
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
$install
->setVerbose($input->getOption('verbose'))
->setDevMode($updateDevMode)
@ -237,9 +248,10 @@ EOT
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setInstall(!$input->getOption('no-install'))
->setUpdateAllowList($packages)
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setRunScripts(!$input->getOption('no-scripts'))
->setDryRun($dryRun)
;

View File

@ -61,14 +61,16 @@ class RequireCommand extends InitCommand
new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'),
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'),
new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'),
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'),
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'),
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
@ -95,7 +97,7 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
if (function_exists('pcntl_async_signals')) {
if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, array($this, 'revertComposerFile'));
pcntl_signal(SIGTERM, array($this, 'revertComposerFile'));
@ -165,7 +167,7 @@ EOT
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
// initialize $this->repos as it is used by the parent InitCommand
$this->repos = new CompositeRepository(array_merge(
array(new PlatformRepository(array(), $platformOverrides)),
array($platformRepo = new PlatformRepository(array(), $platformOverrides)),
$repos
));
@ -175,9 +177,16 @@ EOT
$preferredStability = $composer->getPackage()->getMinimumStability();
}
$phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion();
try {
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed'));
$requirements = $this->determineRequirements(
$input,
$output,
$input->getArgument('packages'),
$platformRepo,
$preferredStability,
!$input->getOption('no-update'),
$input->getOption('fixed')
);
} catch (\Exception $e) {
if ($this->newlyCreated) {
throw new \RuntimeException('No composer.json present in the current directory, this may be the cause of the following exception.', 0, $e);
@ -256,23 +265,31 @@ EOT
$rootPackage->setDevRequires($links['require-dev']);
}
$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');
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
$flags = '';
if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) {
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
$flags .= ' --with-all-dependencies';
} elseif ($input->getOption('update-with-dependencies') || $input->getOption('with-dependencies')) {
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
$flags .= ' --with-dependencies';
}
$io->writeError('<info>Running composer update '.implode(' ', array_keys($requirements)).$flags);
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$install = Installer::create($io, $composer);
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
@ -284,8 +301,9 @@ EOT
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setInstall(!$input->getOption('no-install'))
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;

View File

@ -66,6 +66,7 @@ class ShowCommand extends BaseCommand
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.'),
new InputArgument('version', InputArgument::OPTIONAL, 'Version or version constraint to inspect'),
new InputOption('all', null, InputOption::VALUE_NONE, 'List all packages'),
new InputOption('locked', null, InputOption::VALUE_NONE, 'List all locked packages'),
new InputOption('installed', 'i', InputOption::VALUE_NONE, 'List installed packages only (enabled by default, only present for BC).'),
new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'),
new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'),
@ -149,10 +150,14 @@ EOT
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
}
$platformRepo = new PlatformRepository(array(), $platformOverrides);
$phpVersion = $platformRepo->findPackage('php', '*')->getVersion();
$lockedRepo = null;
if ($input->getOption('self')) {
$package = $this->getComposer()->getPackage();
if ($input->getOption('name-only')) {
$io->write($package->getName());
return 0;
}
$repos = $installedRepo = new InstalledRepository(array(new RootPackageRepository($package)));
} elseif ($input->getOption('platform')) {
$repos = $installedRepo = new InstalledRepository(array($platformRepo));
@ -168,13 +173,27 @@ EOT
}
} elseif ($input->getOption('all') && $composer) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$installedRepo = new InstalledRepository(array($localRepo, $platformRepo));
$locker = $composer->getLocker();
if ($locker->isLocked()) {
$lockedRepo = $locker->getLockedRepository();
$installedRepo = new InstalledRepository(array($lockedRepo, $localRepo, $platformRepo));
} else {
$installedRepo = new InstalledRepository(array($localRepo, $platformRepo));
}
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} elseif ($input->getOption('all')) {
$defaultRepos = RepositoryFactory::defaultRepos($io);
$io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
$installedRepo = new InstalledRepository(array($platformRepo));
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
} elseif ($input->getOption('locked')) {
if (!$composer || !$composer->getLocker()->isLocked()) {
throw new \UnexpectedValueException('A valid composer.json and composer.lock files is required to run this command with --locked');
}
$locker = $composer->getLocker();
$lockedRepo = $locker->getLockedRepository();
$installedRepo = new InstalledRepository(array($lockedRepo));
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$repos = $installedRepo = new InstalledRepository(array($this->getComposer()->getRepositoryManager()->getLocalRepository()));
$rootPkg = $this->getComposer()->getPackage();
@ -229,7 +248,7 @@ EOT
} else {
$latestPackage = null;
if ($input->getOption('latest')) {
$latestPackage = $this->findLatestPackage($package, $composer, $phpVersion);
$latestPackage = $this->findLatestPackage($package, $composer, $platformRepo);
}
if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) {
$exitCode = 1;
@ -315,6 +334,8 @@ EOT
foreach ($repos as $repo) {
if ($repo === $platformRepo) {
$type = 'platform';
} elseif($lockedRepo !== null && $repo === $lockedRepo) {
$type = 'locked';
} elseif ($repo === $installedRepo || in_array($repo, $installedRepo->getRepositories(), true)) {
$type = 'installed';
} else {
@ -351,7 +372,7 @@ EOT
$exitCode = 0;
$viewData = array();
$viewMetaData = array();
foreach (array('platform' => true, 'available' => false, 'installed' => true) as $type => $showVersion) {
foreach (array('platform' => true, 'locked' => true, 'available' => false, 'installed' => true) as $type => $showVersion) {
if (isset($packages[$type])) {
ksort($packages[$type]);
@ -360,7 +381,7 @@ EOT
if ($showLatest && $showVersion) {
foreach ($packages[$type] as $package) {
if (is_object($package)) {
$latestPackage = $this->findLatestPackage($package, $composer, $phpVersion, $showMinorOnly);
$latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMinorOnly);
if ($latestPackage === false) {
continue;
}
@ -1158,18 +1179,18 @@ EOT
/**
* Given a package, this finds the latest package matching it
*
* @param PackageInterface $package
* @param Composer $composer
* @param string $phpVersion
* @param bool $minorOnly
* @param PackageInterface $package
* @param Composer $composer
* @param PlatformRepository $platformRepo
* @param bool $minorOnly
*
* @return PackageInterface|false
*/
private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false)
private function findLatestPackage(PackageInterface $package, Composer $composer, PlatformRepository $platformRepo, $minorOnly = false)
{
// find the latest version allowed in this repo set
$name = $package->getName();
$versionSelector = new VersionSelector($this->getRepositorySet($composer));
$versionSelector = new VersionSelector($this->getRepositorySet($composer), $platformRepo);
$stability = $composer->getPackage()->getMinimumStability();
$flags = $composer->getPackage()->getStabilityFlags();
if (isset($flags[$name])) {
@ -1190,7 +1211,7 @@ EOT
$targetVersion = '^' . $package->getVersion();
}
return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability);
return $versionSelector->findBestCandidate($name, $targetVersion, $bestStability);
}
private function getRepositorySet(Composer $composer)

View File

@ -14,7 +14,7 @@ namespace Composer\Command;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RootPackageRepository;
use Composer\Repository\CompositeRepository;
use Composer\Repository\InstalledRepository;
use Composer\Installer\SuggestedPackagesReporter;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@ -67,16 +67,13 @@ EOT
$installedRepos[] = $composer->getRepositoryManager()->getLocalRepository();
}
$installedRepo = new CompositeRepository($installedRepos);
$installedRepo = new InstalledRepository($installedRepos);
$reporter = new SuggestedPackagesReporter($this->getIO());
$filter = $input->getArgument('packages');
if (empty($filter) && !$input->getOption('all')) {
$filter = array_map(function ($link) {
return $link->getTarget();
}, array_merge($composer->getPackage()->getRequires(), $composer->getPackage()->getDevRequires()));
}
foreach ($installedRepo->getPackages() as $package) {
$packages = $installedRepo->getPackages();
$packages[] = $composer->getPackage();
foreach ($packages as $package) {
if (!empty($filter) && !in_array($package->getName(), $filter)) {
continue;
}
@ -100,7 +97,7 @@ EOT
$mode = SuggestedPackagesReporter::MODE_LIST;
}
$reporter->output($mode, $installedRepo);
$reporter->output($mode, $installedRepo, empty($filter) && !$input->getOption('all') ? $composer->getPackage() : null);
return 0;
}

View File

@ -18,6 +18,9 @@ use Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Package\Version\VersionParser;
use Composer\Semver\Constraint\MultiConstraint;
use Composer\Package\Link;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -39,12 +42,14 @@ class UpdateCommand extends BaseCommand
->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.'),
new InputOption('with', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0'),
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'),
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'),
@ -55,7 +60,8 @@ class UpdateCommand extends BaseCommand
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
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.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'),
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
@ -79,6 +85,14 @@ from a specific vendor:
<info>php composer.phar update vendor/package1 foo/* [...]</info>
To run an update with more restrictive constraints you can use:
<info>php composer.phar update --with vendor/package:1.0.*</info>
To run a partial update with more restrictive constraints you can use the shorthand:
<info>php composer.phar update vendor/package:1.0.*</info>
To select packages names interactively with auto-completion use <info>-i</info>.
Read more at https://getcomposer.org/doc/03-cli.md#update-u
@ -100,22 +114,53 @@ EOT
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$packages = $input->getArgument('packages');
$reqs = $this->formatRequirements($input->getOption('with'));
// extract --with shorthands from the allowlist
if ($packages) {
$allowlistPackagesWithRequirements = array_filter($packages, function ($pkg) {
return preg_match('{\S+[ =:]\S+}', $pkg) > 0;
});
foreach ($this->formatRequirements($allowlistPackagesWithRequirements) as $package => $constraint) {
$reqs[$package] = $constraint;
}
// replace the foo/bar:req by foo/bar in the allowlist
foreach ($allowlistPackagesWithRequirements as $package) {
$packageName = preg_replace('{^([^ =:]+)[ =:].*$}', '$1', $package);
$index = array_search($package, $packages);
$packages[$index] = $packageName;
}
}
$rootRequires = $composer->getPackage()->getRequires();
$rootDevRequires = $composer->getPackage()->getDevRequires();
foreach ($reqs as $package => $constraint) {
if (isset($rootRequires[$package])) {
$rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint);
} elseif (isset($rootDevRequires[$package])) {
$rootDevRequires[$package] = $this->appendConstraintToLink($rootDevRequires[$package], $constraint);
} else {
throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one');
}
}
$composer->getPackage()->setRequires($rootRequires);
$composer->getPackage()->setDevRequires($rootDevRequires);
if ($input->getOption('interactive')) {
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
}
if ($input->getOption('root-reqs')) {
$require = array_keys($composer->getPackage()->getRequires());
$requires = array_keys($rootRequires);
if (!$input->getOption('no-dev')) {
$requireDev = array_keys($composer->getPackage()->getDevRequires());
$require = array_merge($require, $requireDev);
$requires = array_merge($requires, array_keys($rootDevRequires));
}
if (!empty($packages)) {
$packages = array_intersect($packages, $require);
$packages = array_intersect($packages, $requires);
} else {
$packages = $require;
$packages = $requires;
}
}
@ -151,6 +196,8 @@ EOT
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
}
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
@ -163,10 +210,11 @@ EOT
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setInstall(!$input->getOption('no-install'))
->setUpdateMirrors($updateMirrors)
->setUpdateAllowList($packages)
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;
@ -240,4 +288,19 @@ EOT
throw new \RuntimeException('Installation aborted.');
}
private function appendConstraintToLink(Link $link, $constraint)
{
$parser = new VersionParser;
$oldPrettyString = $link->getConstraint()->getPrettyString();
$newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint)), true);
$newConstraint->setPrettyString($oldPrettyString.', '.$constraint);
return new Link(
$link->getSource(),
$link->getTarget(),
$newConstraint,
$link->getDescription(),
$link->getPrettyConstraint() . ', ' . $constraint
);
}
}

View File

@ -42,6 +42,7 @@ class ValidateCommand extends BaseCommand
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('no-check-version', null, InputOption::VALUE_NONE, 'Do not report a warning if the version field is present'),
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'),
@ -86,8 +87,9 @@ EOT
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
$checkPublish = !$input->getOption('no-check-publish');
$checkLock = !$input->getOption('no-check-lock');
$checkVersion = $input->getOption('no-check-version') ? 0 : ConfigValidator::CHECK_VERSION;
$isStrict = $input->getOption('strict');
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion);
$lockErrors = array();
$composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins'));
@ -107,7 +109,7 @@ EOT
$path = $composer->getInstallationManager()->getInstallPath($package);
$file = $path . '/composer.json';
if (is_dir($path) && file_exists($file)) {
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion);
$this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors);

View File

@ -139,6 +139,11 @@ class Compiler
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/installed.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/InstalledVersions.php'));
if (file_exists(__DIR__.'/../../vendor/composer/platform_check.php')) {
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/platform_check.php'));
}
if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) {
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php'));
}

View File

@ -14,6 +14,7 @@ namespace Composer;
use Composer\Package\RootPackageInterface;
use Composer\Package\Locker;
use Composer\Util\Loop;
use Composer\Repository\RepositoryManager;
use Composer\Installer\InstallationManager;
use Composer\Plugin\PluginManager;
@ -55,6 +56,17 @@ class Composer
const RELEASE_DATE = '@release_date@';
const SOURCE_VERSION = '2.0-dev+source';
/**
* Version number of the internal composer-runtime-api package
*
* This is used to version features available to projects at runtime
* like the platform-check file, the Composer\InstalledVersions class
* and possibly others in the future.
*
* @var string
*/
const RUNTIME_API_VERSION = '2.0.0';
public static function getVersion()
{
// no replacement done, this must be a source checkout
@ -71,7 +83,7 @@ class Composer
}
/**
* @var Package\RootPackageInterface
* @var RootPackageInterface
*/
private $package;
@ -80,6 +92,11 @@ class Composer
*/
private $locker;
/**
* @var Loop
*/
private $loop;
/**
* @var Repository\RepositoryManager
*/
@ -121,7 +138,7 @@ class Composer
private $archiveManager;
/**
* @param Package\RootPackageInterface $package
* @param RootPackageInterface $package
* @return void
*/
public function setPackage(RootPackageInterface $package)
@ -130,7 +147,7 @@ class Composer
}
/**
* @return Package\RootPackageInterface
* @return RootPackageInterface
*/
public function getPackage()
{
@ -154,7 +171,7 @@ class Composer
}
/**
* @param Package\Locker $locker
* @param Locker $locker
*/
public function setLocker(Locker $locker)
{
@ -162,7 +179,7 @@ class Composer
}
/**
* @return Package\Locker
* @return Locker
*/
public function getLocker()
{
@ -170,7 +187,23 @@ class Composer
}
/**
* @param Repository\RepositoryManager $manager
* @param Loop $loop
*/
public function setLoop(Loop $loop)
{
$this->loop = $loop;
}
/**
* @return Loop
*/
public function getLoop()
{
return $this->loop;
}
/**
* @param RepositoryManager $manager
*/
public function setRepositoryManager(RepositoryManager $manager)
{
@ -178,7 +211,7 @@ class Composer
}
/**
* @return Repository\RepositoryManager
* @return RepositoryManager
*/
public function getRepositoryManager()
{
@ -186,7 +219,7 @@ class Composer
}
/**
* @param Downloader\DownloadManager $manager
* @param DownloadManager $manager
*/
public function setDownloadManager(DownloadManager $manager)
{
@ -194,7 +227,7 @@ class Composer
}
/**
* @return Downloader\DownloadManager
* @return DownloadManager
*/
public function getDownloadManager()
{
@ -218,7 +251,7 @@ class Composer
}
/**
* @param Installer\InstallationManager $manager
* @param InstallationManager $manager
*/
public function setInstallationManager(InstallationManager $manager)
{
@ -226,7 +259,7 @@ class Composer
}
/**
* @return Installer\InstallationManager
* @return InstallationManager
*/
public function getInstallationManager()
{
@ -234,7 +267,7 @@ class Composer
}
/**
* @param Plugin\PluginManager $manager
* @param PluginManager $manager
*/
public function setPluginManager(PluginManager $manager)
{
@ -242,7 +275,7 @@ class Composer
}
/**
* @return Plugin\PluginManager
* @return PluginManager
*/
public function getPluginManager()
{
@ -266,7 +299,7 @@ class Composer
}
/**
* @param Autoload\AutoloadGenerator $autoloadGenerator
* @param AutoloadGenerator $autoloadGenerator
*/
public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator)
{
@ -274,7 +307,7 @@ class Composer
}
/**
* @return Autoload\AutoloadGenerator
* @return AutoloadGenerator
*/
public function getAutoloadGenerator()
{

View File

@ -64,6 +64,7 @@ class Config
'htaccess-protect' => true,
'use-github-api' => true,
'lock' => true,
'platform-check' => true,
// valid keys without defaults (auth config stuff):
// bitbucket-oauth
// github-oauth

View File

@ -108,17 +108,17 @@ class Decisions implements \Iterator, \Countable
public function validOffset($queueOffset)
{
return $queueOffset >= 0 && $queueOffset < count($this->decisionQueue);
return $queueOffset >= 0 && $queueOffset < \count($this->decisionQueue);
}
public function lastReason()
{
return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_REASON];
return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_REASON];
}
public function lastLiteral()
{
return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_LITERAL];
return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_LITERAL];
}
public function reset()
@ -130,7 +130,7 @@ class Decisions implements \Iterator, \Countable
public function resetToOffset($offset)
{
while (count($this->decisionQueue) > $offset + 1) {
while (\count($this->decisionQueue) > $offset + 1) {
$decision = array_pop($this->decisionQueue);
$this->decisionMap[abs($decision[self::DECISION_LITERAL])] = 0;
}
@ -144,7 +144,7 @@ class Decisions implements \Iterator, \Countable
public function count()
{
return count($this->decisionQueue);
return \count($this->decisionQueue);
}
public function rewind()
@ -174,7 +174,7 @@ class Decisions implements \Iterator, \Countable
public function isEmpty()
{
return count($this->decisionQueue) === 0;
return \count($this->decisionQueue) === 0;
}
protected function addDecision($literal, $level)

View File

@ -60,7 +60,7 @@ class DefaultPolicy implements PolicyInterface
$sortedLiterals = $this->pruneRemoteAliases($pool, $sortedLiterals);
}
$selected = call_user_func_array('array_merge', $packages);
$selected = \call_user_func_array('array_merge', $packages);
// now sort the result across all packages to respect replaces across packages
usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) {

View File

@ -64,7 +64,7 @@ class GenericRule extends Rule
public function isAssertion()
{
return 1 === count($this->literals);
return 1 === \count($this->literals);
}
/**

View File

@ -33,7 +33,7 @@ class MultiConflictRule extends Rule
{
parent::__construct($reason, $reasonData);
if (count($literals) < 3) {
if (\count($literals) < 3) {
throw new \RuntimeException("multi conflict rule requires at least 3 literals");
}

View File

@ -42,10 +42,4 @@ abstract class SolverOperation implements OperationInterface
{
return $this->reason;
}
/**
* @param $lock bool Whether this is an operation on the lock file
* @return string
*/
abstract public function show($lock);
}

View File

@ -16,7 +16,6 @@ use Composer\Package\AliasPackage;
use Composer\Package\Version\VersionParser;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\EmptyConstraint;
use Composer\Package\PackageInterface;
/**
@ -71,7 +70,7 @@ class Pool implements \Countable
*/
public function count()
{
return count($this->packages);
return \count($this->packages);
}
/**
@ -189,6 +188,6 @@ class Pool implements \Countable
public function isUnacceptableFixedPackage(PackageInterface $package)
{
return in_array($package, $this->unacceptableFixedPackages, true);
return \in_array($package, $this->unacceptableFixedPackages, true);
}
}

View File

@ -21,7 +21,8 @@ use Composer\Package\Version\StabilityFilter;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RootPackageRepository;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\EmptyConstraint;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Semver\Constraint\MultiConstraint;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Plugin\PrePoolCreateEvent;
@ -32,23 +33,53 @@ use Composer\Plugin\PluginEvents;
*/
class PoolBuilder
{
/**
* @var int[]
*/
private $acceptableStabilities;
/**
* @var int[]
*/
private $stabilityFlags;
/**
* @psalm-var array<string, array<string, array{alias: string, alias_normalized: string}>>
*/
private $rootAliases;
/**
* @psalm-var array<string, string>
*/
private $rootReferences;
/**
* @var EventDispatcher
*/
private $eventDispatcher;
/**
* @var IOInterface
*/
private $io;
/**
* @psalm-var array<string, AliasPackage>
*/
private $aliasMap = array();
/**
* @psalm-var array<string, ConstraintInterface[]|null>
*/
private $nameConstraints = array();
private $loadedNames = array();
/**
* @psalm-var Package[]
*/
private $packages = array();
/**
* @psalm-var list<Package>
*/
private $unacceptableFixedPackages = array();
private $updateAllowList = array();
private $skippedLoad = array();
/**
* @psalm-var array<string, bool>
*/
private $updateAllowWarned = array();
/**
@ -82,7 +113,7 @@ class PoolBuilder
$request->fixPackage($lockedPackage);
$lockedName = $lockedPackage->getName();
// remember which packages we skipped loading remote content for in this partial update
$this->skippedLoad[$lockedPackage->getName()] = $lockedName;
$this->skippedLoad[$lockedName] = $lockedName;
foreach ($lockedPackage->getReplaces() as $link) {
$this->skippedLoad[$link->getTarget()] = $lockedName;
}
@ -122,7 +153,7 @@ class PoolBuilder
}
$loadNames[$packageName] = $constraint;
$this->nameConstraints[$packageName] = $constraint ? new MultiConstraint(array($constraint), false) : null;
$this->nameConstraints[$packageName] = $constraint && !($constraint instanceof MatchAllConstraint) ? array($constraint) : null;
}
// clean up loadNames for anything we manually marked loaded above
@ -159,11 +190,17 @@ class PoolBuilder
}
// filter packages according to all the require statements collected for each package
$nameConstraints = array();
foreach ($this->nameConstraints as $name => $constraints) {
if (\is_array($constraints)) {
$nameConstraints[$name] = MultiConstraint::create(array_values(array_unique($constraints)), false);
}
}
foreach ($this->packages as $i => $package) {
// we check all alias related packages at once, so no need to check individual aliases
// isset also checks non-null value
if (!$package instanceof AliasPackage && isset($this->nameConstraints[$package->getName()])) {
$constraint = $this->nameConstraints[$package->getName()];
if (!$package instanceof AliasPackage && isset($nameConstraints[$package->getName()])) {
$constraint = $nameConstraints[$package->getName()];
$aliasedPackages = array($i => $package);
if (isset($this->aliasMap[spl_object_hash($package)])) {
@ -268,12 +305,11 @@ class PoolBuilder
}
$linkConstraint = $link->getConstraint();
if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) {
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);
if ($linkConstraint && !($linkConstraint instanceof MatchAllConstraint)) {
if (!\array_key_exists($require, $this->nameConstraints)) {
$this->nameConstraints[$require] = array($linkConstraint);
} elseif (\is_array($this->nameConstraints[$require])) {
$this->nameConstraints[$require][] = $linkConstraint;
}
// else it is null and should stay null
} else {
@ -377,8 +413,12 @@ class PoolBuilder
}
}
// if we unfixed a replaced package name, we also need to unfix the replacer itself
if ($this->skippedLoad[$name] !== $name) {
if (
// if we unfixed a replaced package name, we also need to unfix the replacer itself
$this->skippedLoad[$name] !== $name
// as long as it was not unfixed yet
&& isset($this->skippedLoad[$this->skippedLoad[$name]])
) {
$this->unfixPackage($request, $this->skippedLoad[$name]);
}

View File

@ -66,7 +66,7 @@ class Problem
* @param array $installedMap A map of all present packages
* @return string
*/
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array())
{
// TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections?
$reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
@ -90,23 +90,62 @@ class Problem
}
if (empty($packages)) {
return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $packageName, $constraint));
return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $packageName, $constraint));
}
}
$messages = array();
foreach ($reasons as $rule) {
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
}
return "\n - ".implode("\n - ", array_unique($messages));
return self::formatDeduplicatedRules($reasons, ' ', $repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
}
public function isCausedByLock()
/**
* @internal
*/
public static function formatDeduplicatedRules($rules, $indent, RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array())
{
$messages = array();
$templates = array();
$parser = new VersionParser;
$deduplicatableRuleTypes = array(Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT);
foreach ($rules as $rule) {
$message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && preg_match('{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}', $message, $m)) {
$template = preg_replace('{^\S+ \S+ }', '%s%s ', $message);
$messages[] = $template;
$templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2];
} elseif ($message !== '') {
$messages[] = $message;
}
}
$result = array();
foreach (array_unique($messages) as $message) {
if (isset($templates[$message])) {
foreach ($templates[$message] as $package => $versions) {
uksort($versions, 'version_compare');
if (!$isVerbose) {
$versions = self::condenseVersionList($versions, 1);
}
if (count($versions) > 1) {
// remove the s from requires/conflicts to correct grammar
$message = preg_replace('{^(%s%s (?:require|conflict))s}', '$1', $message);
$result[] = sprintf($message, $package, '['.implode(', ', $versions).']');
} else {
$result[] = sprintf($message, $package, ' '.reset($versions));
}
}
} else {
$result[] = $message;
}
}
return "\n$indent- ".implode("\n$indent- ", $result);
}
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
{
foreach ($this->reasons as $sectionRules) {
foreach ($sectionRules as $rule) {
if ($rule->isCausedByLock()) {
if ($rule->isCausedByLock($repositorySet, $request, $pool)) {
return true;
}
}
@ -138,7 +177,7 @@ class Problem
/**
* @internal
*/
public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $packageName, $constraint = null)
public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $packageName, $constraint = null)
{
// handle php/hhvm
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
@ -210,7 +249,7 @@ class Problem
return $rootReqs[$packageName]->matches(new Constraint('==', $p->getVersion()));
});
if (0 === count($filtered)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').');
}
}
@ -220,7 +259,7 @@ class Problem
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
});
if (0 === count($filtered)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
}
}
@ -229,15 +268,15 @@ class Problem
});
if (!$nonLockedPackages) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.');
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with another require.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with another require.');
}
// check if the package is found when bypassing stability checks
if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
}
// check if the package is found when bypassing the constraint check
@ -257,10 +296,10 @@ class Problem
}
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your constraint.');
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.');
}
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
@ -287,7 +326,7 @@ class Problem
/**
* @internal
*/
public static function getPackageList(array $packages)
public static function getPackageList(array $packages, $isVerbose)
{
$prepared = array();
foreach ($packages as $package) {
@ -299,12 +338,46 @@ class Problem
if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) {
unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]);
}
uksort($package['versions'], 'version_compare');
if (!$isVerbose) {
$package['versions'] = self::condenseVersionList($package['versions'], 4);
}
$prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
}
return implode(', ', $prepared);
}
/**
* @param string[] $versions an array of pretty versions, with normalized versions as keys
* @return list<string> a list of pretty versions and '...' where versions were removed
*/
private static function condenseVersionList(array $versions, $max)
{
if (count($versions) <= $max) {
return $versions;
}
$filtered = array();
$byMajor = array();
foreach ($versions as $version => $pretty) {
$byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty;
}
foreach ($byMajor as $versionsForMajor) {
if (count($versionsForMajor) > $max) {
$filtered[] = $versionsForMajor[0];
$filtered[] = '...';
$filtered[] = $versionsForMajor[count($versionsForMajor) - 1];
} else {
$filtered = array_merge($filtered, $versionsForMajor);
}
}
return $filtered;
}
private static function hasMultipleNames(array $packages)
{
$name = null;

View File

@ -17,7 +17,9 @@ use Composer\Package\Link;
use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage;
use Composer\Repository\RepositorySet;
use Composer\Repository\PlatformRepository;
use Composer\Package\Version\VersionParser;
use Composer\Semver\Constraint\Constraint;
/**
* @author Nils Adermann <naderman@naderman.de>
@ -26,7 +28,6 @@ use Composer\Package\Version\VersionParser;
abstract class Rule
{
// reason constants
const RULE_INTERNAL_ALLOW_UPDATE = 1;
const RULE_ROOT_REQUIRE = 2;
const RULE_FIXED = 3;
const RULE_PACKAGE_CONFLICT = 6;
@ -122,27 +123,54 @@ abstract class Rule
abstract public function isAssertion();
public function isCausedByLock()
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
{
return $this->getReason() === self::RULE_FIXED && $this->reasonData['lockable'];
if ($this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']) {
return true;
}
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $this->reasonData->getTarget())) {
return false;
}
foreach ($request->getFixedPackages() as $package) {
if ($package->getName() === $this->reasonData->getTarget()) {
if ($pool->isUnacceptableFixedPackage($package)) {
return true;
}
if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) {
return true;
}
break;
}
}
}
if ($this->getReason() === self::RULE_ROOT_REQUIRE) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $this->reasonData['packageName'])) {
return false;
}
foreach ($request->getFixedPackages() as $package) {
if ($package->getName() === $this->reasonData['packageName']) {
if ($pool->isUnacceptableFixedPackage($package)) {
return true;
}
if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) {
return true;
}
break;
}
}
}
return false;
}
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array())
{
$literals = $this->getLiterals();
$ruleText = '';
foreach ($literals as $i => $literal) {
if ($i != 0) {
$ruleText .= '|';
}
$ruleText .= $pool->literalToPrettyString($literal, $installedMap);
}
switch ($this->getReason()) {
case self::RULE_INTERNAL_ALLOW_UPDATE:
return $ruleText;
case self::RULE_ROOT_REQUIRE:
$packageName = $this->reasonData['packageName'];
$constraint = $this->reasonData['constraint'];
@ -152,7 +180,7 @@ abstract class Rule
return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '');
}
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages).'.';
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
case self::RULE_FIXED:
$package = $this->deduplicateMasterAlias($this->reasonData['package']);
@ -179,11 +207,11 @@ abstract class Rule
$text = $this->reasonData->getPrettyString($sourcePackage);
if ($requires) {
$text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires) . '.';
$text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires, $isVerbose) . '.';
} else {
$targetName = $this->reasonData->getTarget();
$reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $targetName, $this->reasonData->getConstraint());
$reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $targetName, $this->reasonData->getConstraint());
return $text . ' -> ' . $reason[1];
}
@ -227,29 +255,61 @@ abstract class Rule
}
if ($installedPackages && $removablePackages) {
return $this->formatPackagesUnique($pool, $removablePackages).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages).'. '.$reason;
return $this->formatPackagesUnique($pool, $removablePackages, $isVerbose).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages, $isVerbose).'. '.$reason;
}
return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals).'. '.$reason;
return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals, $isVerbose).'. '.$reason;
}
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.';
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.';
case self::RULE_LEARNED:
if (isset($learnedPool[$this->reasonData])) {
$learnedString = ', learned rules:'."\n - ";
$reasons = array();
foreach ($learnedPool[$this->reasonData] as $learnedRule) {
$reasons[] = $learnedRule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
}
$learnedString .= implode("\n - ", array_unique($reasons));
$learnedString = ', learned rules:' . Problem::formatDeduplicatedRules($learnedPool[$this->reasonData], ' ', $repositorySet, $request, $pool, $installedMap, $learnedPool);
} else {
$learnedString = ' (reasoning unavailable)';
}
if (count($literals) === 1) {
$ruleText = $pool->literalToPrettyString($literals[0], $installedMap);
} else {
$groups = array();
foreach ($literals as $literal) {
$package = $pool->literalToPackage($literal);
if (isset($installedMap[$package->id])) {
$group = $literal > 0 ? 'keep' : 'remove';
} else {
$group = $literal > 0 ? 'install' : 'don\'t install';
}
$groups[$group][] = $this->deduplicateMasterAlias($package);
}
$ruleTexts = array();
foreach ($groups as $group => $packages) {
$ruleTexts[] = $group . (count($packages) > 1 ? ' one of' : '').' ' . $this->formatPackagesUnique($pool, $packages, $isVerbose);
}
$ruleText = implode(' | ', $ruleTexts);
}
return 'Conclusion: '.$ruleText.$learnedString;
case self::RULE_PACKAGE_ALIAS:
return $ruleText;
$aliasPackage = $pool->literalToPackage($literals[0]);
// avoid returning content like "9999999-dev is an alias of dev-master" as it is useless
if ($aliasPackage->getVersion() === VersionParser::DEV_MASTER_ALIAS) {
return '';
}
$package = $this->deduplicateMasterAlias($pool->literalToPackage($literals[1]));
return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.';
default:
$ruleText = '';
foreach ($literals as $i => $literal) {
if ($i != 0) {
$ruleText .= '|';
}
$ruleText .= $pool->literalToPrettyString($literal, $installedMap);
}
return '('.$ruleText.')';
}
}
@ -260,16 +320,16 @@ abstract class Rule
*
* @return string
*/
protected function formatPackagesUnique($pool, array $packages)
protected function formatPackagesUnique($pool, array $packages, $isVerbose)
{
$prepared = array();
foreach ($packages as $index => $package) {
if (!is_object($package)) {
if (!\is_object($package)) {
$packages[$index] = $pool->literalToPackage($package);
}
}
return Problem::getPackageList($packages);
return Problem::getPackageList($packages, $isVerbose);
}
private function getReplacedNames(PackageInterface $package)

View File

@ -76,7 +76,7 @@ class Rule2Literals extends Rule
}
$literals = $rule->getLiterals();
if (2 != count($literals)) {
if (2 != \count($literals)) {
return false;
}

View File

@ -65,7 +65,7 @@ class RuleSet implements \IteratorAggregate, \Countable
// Do not add if rule already exists
if (isset($this->rulesByHash[$hash])) {
$potentialDuplicates = $this->rulesByHash[$hash];
if (is_array($potentialDuplicates)) {
if (\is_array($potentialDuplicates)) {
foreach ($potentialDuplicates as $potentialDuplicate) {
if ($rule->equals($potentialDuplicate)) {
return;
@ -90,7 +90,7 @@ class RuleSet implements \IteratorAggregate, \Countable
if (!isset($this->rulesByHash[$hash])) {
$this->rulesByHash[$hash] = $rule;
} elseif (is_array($this->rulesByHash[$hash])) {
} elseif (\is_array($this->rulesByHash[$hash])) {
$this->rulesByHash[$hash][] = $rule;
} else {
$originalRule = $this->rulesByHash[$hash];
@ -120,7 +120,7 @@ class RuleSet implements \IteratorAggregate, \Countable
public function getIteratorFor($types)
{
if (!is_array($types)) {
if (!\is_array($types)) {
$types = array($types);
}
@ -136,7 +136,7 @@ class RuleSet implements \IteratorAggregate, \Countable
public function getIteratorWithout($types)
{
if (!is_array($types)) {
if (!\is_array($types)) {
$types = array($types);
}
@ -157,13 +157,13 @@ class RuleSet implements \IteratorAggregate, \Countable
return array_keys($types);
}
public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null)
public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null, $isVerbose = false)
{
$string = "\n";
foreach ($this->rules as $type => $rules) {
$string .= str_pad(self::$types[$type], 8, ' ') . ": ";
foreach ($rules as $rule) {
$string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool) : $rule)."\n";
$string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose) : $rule)."\n";
}
$string .= "\n\n";
}

View File

@ -120,7 +120,7 @@ class RuleSetGenerator
$literals[] = -$package->id;
}
if (count($literals) == 2) {
if (\count($literals) == 2) {
return new Rule2Literals($literals[0], $literals[1], $reason, $reasonData);
}
@ -165,11 +165,18 @@ class RuleSetGenerator
$this->addedPackagesByNames[$name][] = $package;
}
} else {
$workQueue->enqueue($package->getAliasOf());
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package));
// if alias package has no self.version requires, its requirements do not
// need to be added as the aliased package processing will take care of it
if (!$package->hasSelfVersionRequires()) {
continue;
}
}
foreach ($package->getRequires() as $link) {
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
continue;
}
@ -193,7 +200,7 @@ class RuleSetGenerator
continue;
}
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
continue;
}
@ -207,27 +214,13 @@ class RuleSetGenerator
}
foreach ($this->addedPackagesByNames as $name => $packages) {
if (count($packages) > 1) {
if (\count($packages) > 1) {
$reason = Rule::RULE_PACKAGE_SAME_NAME;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, $name));
}
}
}
protected function obsoleteImpossibleForAlias($package, $provider)
{
$packageIsAlias = $package instanceof AliasPackage;
$providerIsAlias = $provider instanceof AliasPackage;
$impossible = (
($packageIsAlias && $package->getAliasOf() === $provider) ||
($providerIsAlias && $provider->getAliasOf() === $package) ||
($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
);
return $impossible;
}
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
{
$unlockableMap = $request->getUnlockableMap();
@ -253,7 +246,7 @@ class RuleSetGenerator
}
foreach ($request->getRequires() as $packageName => $constraint) {
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) {
continue;
}
@ -272,6 +265,9 @@ class RuleSetGenerator
}
}
/**
* @param bool|array $ignorePlatformReqs
*/
public function getRulesFor(Request $request, $ignorePlatformReqs = false)
{
$this->rules = new RuleSet;

View File

@ -51,7 +51,7 @@ class RuleSetIterator implements \Iterator
return;
}
if ($this->currentOffset >= count($this->rules[$this->currentType])) {
if ($this->currentOffset >= \count($this->rules[$this->currentType])) {
$this->currentOffset = 0;
do {
@ -63,7 +63,7 @@ class RuleSetIterator implements \Iterator
}
$this->currentType = $this->types[$this->currentTypeOffset];
} while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType]));
} while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType]));
}
}
@ -83,7 +83,7 @@ class RuleSetIterator implements \Iterator
}
$this->currentType = $this->types[$this->currentTypeOffset];
} while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType]));
} while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType]));
}
public function valid()

View File

@ -37,7 +37,7 @@ class RuleWatchNode
$literals = $rule->getLiterals();
$literalCount = count($literals);
$literalCount = \count($literals);
$this->watch1 = $literalCount > 0 ? $literals[0] : 0;
$this->watch2 = $literalCount > 1 ? $literals[1] : 0;
}
@ -55,7 +55,7 @@ class RuleWatchNode
$literals = $this->rule->getLiterals();
// if there are only 2 elements, both are being watched anyway
if (count($literals) < 3 || $this->rule instanceof MultiConflictRule) {
if (\count($literals) < 3 || $this->rule instanceof MultiConflictRule) {
return;
}

View File

@ -31,8 +31,6 @@ class Solver
/** @var RuleSet */
protected $rules;
/** @var RuleSetGenerator */
protected $ruleSetGenerator;
/** @var RuleWatchGraph */
protected $watchGraph;
@ -75,7 +73,7 @@ class Solver
*/
public function getRuleSetSize()
{
return count($this->rules);
return \count($this->rules);
}
public function getPool()
@ -87,9 +85,9 @@ class Solver
private function makeAssertionRuleDecisions()
{
$decisionStart = count($this->decisions) - 1;
$decisionStart = \count($this->decisions) - 1;
$rulesCount = count($this->rules);
$rulesCount = \count($this->rules);
for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) {
$rule = $this->rules->ruleById[$ruleIndex];
@ -164,13 +162,13 @@ class Solver
}
/**
* @param Request $request
* @param bool $ignorePlatformReqs
* @param Request $request
* @param bool|array $ignorePlatformReqs
*/
protected function checkForRootRequireProblems($request, $ignorePlatformReqs)
protected function checkForRootRequireProblems(Request $request, $ignorePlatformReqs)
{
foreach ($request->getRequires() as $packageName => $constraint) {
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) {
continue;
}
@ -183,8 +181,8 @@ class Solver
}
/**
* @param Request $request
* @param bool $ignorePlatformReqs
* @param Request $request
* @param bool|array $ignorePlatformReqs
* @return LockTransaction
*/
public function solve(Request $request, $ignorePlatformReqs = false)
@ -192,8 +190,9 @@ class Solver
$this->setupFixedMap($request);
$this->io->writeError('Generating rules', true, IOInterface::DEBUG);
$this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool);
$this->rules = $this->ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs);
$ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool);
$this->rules = $ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs);
unset($ruleSetGenerator);
$this->checkForRootRequireProblems($request, $ignorePlatformReqs);
$this->decisions = new Decisions($this->pool);
$this->watchGraph = new RuleWatchGraph;
@ -269,10 +268,10 @@ class Solver
}
$this->decisions->revertLast();
$this->propagateIndex = count($this->decisions);
$this->propagateIndex = \count($this->decisions);
}
while (!empty($this->branches) && $this->branches[count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) {
while (!empty($this->branches) && $this->branches[\count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) {
array_pop($this->branches);
}
}
@ -357,7 +356,7 @@ class Solver
$selectedLiteral = array_shift($literals);
// if there are multiple candidates, then branch
if (count($literals)) {
if (\count($literals)) {
$this->branches[] = array($literals, $level);
}
@ -378,12 +377,12 @@ class Solver
$seen = array();
$learnedLiterals = array(null);
$decisionId = count($this->decisions);
$decisionId = \count($this->decisions);
$this->learnedPool[] = array();
while (true) {
$this->learnedPool[count($this->learnedPool) - 1][] = $rule;
$this->learnedPool[\count($this->learnedPool) - 1][] = $rule;
foreach ($rule->getLiterals() as $literal) {
// skip the one true literal
@ -466,7 +465,7 @@ class Solver
$rule = $decision[Decisions::DECISION_REASON];
}
$why = count($this->learnedPool) - 1;
$why = \count($this->learnedPool) - 1;
if (!$learnedLiterals[0]) {
throw new SolverBugException(
@ -647,7 +646,7 @@ class Solver
}
}
if ($noneSatisfied && count($decisionQueue)) {
if ($noneSatisfied && \count($decisionQueue)) {
// if any of the options in the decision queue are fixed, only use those
$prunedQueue = array();
foreach ($decisionQueue as $literal) {
@ -660,7 +659,7 @@ class Solver
}
}
if ($noneSatisfied && count($decisionQueue)) {
if ($noneSatisfied && \count($decisionQueue)) {
$oLevel = $level;
$level = $this->selectAndInstall($level, $decisionQueue, $rule);
@ -687,7 +686,7 @@ class Solver
$systemLevel = $level;
}
$rulesCount = count($this->rules);
$rulesCount = \count($this->rules);
$pass = 1;
$this->io->writeError('Looking at all rules.', true, IOInterface::DEBUG);
@ -734,7 +733,7 @@ class Solver
}
// need to have at least 2 item to pick from
if (count($decisionQueue) < 2) {
if (\count($decisionQueue) < 2) {
continue;
}
@ -745,7 +744,7 @@ class Solver
}
// something changed, so look at all rules again
$rulesCount = count($this->rules);
$rulesCount = \count($this->rules);
$n = -1;
}
@ -754,13 +753,13 @@ class Solver
}
// minimization step
if (count($this->branches)) {
if (\count($this->branches)) {
$lastLiteral = null;
$lastLevel = null;
$lastBranchIndex = 0;
$lastBranchOffset = 0;
for ($i = count($this->branches) - 1; $i >= 0; $i--) {
for ($i = \count($this->branches) - 1; $i >= 0; $i--) {
list($literals, $l) = $this->branches[$i];
foreach ($literals as $offset => $literal) {

View File

@ -31,7 +31,7 @@ class SolverProblemsException extends \RuntimeException
parent::__construct('Failed resolving dependencies with '.count($problems).' problems, call getPrettyString to get formatted details', 2);
}
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isDevExtraction = false)
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $isDevExtraction = false)
{
$installedMap = $request->getPresentMap(true);
$hasExtensionProblems = false;
@ -39,13 +39,13 @@ class SolverProblemsException extends \RuntimeException
$problems = array();
foreach ($this->problems as $problem) {
$problems[] = $problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n";
$problems[] = $problem->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $this->learnedPool)."\n";
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
$hasExtensionProblems = true;
}
$isCausedByLock |= $problem->isCausedByLock();
$isCausedByLock |= $problem->isCausedByLock($repositorySet, $request, $pool);
}
$i = 1;
@ -54,27 +54,36 @@ class SolverProblemsException extends \RuntimeException
$text .= " Problem ".($i++).$problem;
}
$hints = array();
if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) {
$text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
$hints[] = "Potential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
}
if ($hasExtensionProblems) {
$text .= $this->createExtensionHint();
$hints[] = $this->createExtensionHint();
}
if ($isCausedByLock && !$isDevExtraction) {
$text .= "\nUse the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions.";
$hints[] = "Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions.";
}
if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match') && strpos($text, '- ocramius/package-versions')) {
$hints[] = "<warning>ocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.</warning>\nIf you can not upgrade PHP you can require <info>composer/package-versions-deprecated</info> to resolve this with PHP 7.0+.";
}
// TODO remove before 2.0 final
if (!class_exists('PHPUnit\Framework\TestCase', false)) {
if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) {
$text .= "\nYou are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs, but this will also ignore your PHP version and may result in bigger problems down the line.";
$hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-req=composer-plugin-api, but this may result in broken plugins and bigger problems down the line.";
} else {
$text .= "\nYou are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2.";
$hints[] = "You are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2.";
}
}
if ($hints) {
$text .= "\n" . implode("\n\n", $hints);
}
return $text;
}
@ -91,9 +100,9 @@ class SolverProblemsException extends \RuntimeException
return '';
}
$text = "\n To enable extensions, verify that they are enabled in your .ini files:\n - ";
$text = "To enable extensions, verify that they are enabled in your .ini files:\n - ";
$text .= implode("\n - ", $paths);
$text .= "\n You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.";
$text .= "\nYou can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.";
return $text;
}

View File

@ -261,9 +261,9 @@ class Transaction
// is this a plugin or a dependency of a plugin?
if ($isPlugin || count(array_intersect($package->getNames(), $pluginRequires))) {
// get the package's requires, but filter out any platform requirements or 'composer-plugin-api'
// get the package's requires, but filter out any platform requirements
$requires = array_filter(array_keys($package->getRequires()), function ($req) {
return $req !== 'composer-plugin-api' && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req);
return !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req);
});
// is this a plugin with no meaningful dependencies?
@ -295,7 +295,7 @@ class Transaction
{
$uninstOps = array();
foreach ($operations as $idx => $op) {
if ($op instanceof Operation\UninstallOperation) {
if ($op instanceof Operation\UninstallOperation || $op instanceof Operation\MarkAliasUninstalledOperation) {
$uninstOps[] = $op;
unset($operations[$idx]);
}

View File

@ -15,6 +15,7 @@ namespace Composer\Downloader;
use Composer\Package\PackageInterface;
use Symfony\Component\Finder\Finder;
use Composer\IO\IOInterface;
use Composer\Exception\IrrecoverableDownloadException;
/**
* Base downloader for archives
@ -25,6 +26,18 @@ use Composer\IO\IOInterface;
*/
abstract class ArchiveDownloader extends FileDownloader
{
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
{
$res = parent::download($package, $path, $prevPackage, $output);
// if not downgrading and the dir already exists it seems we have an inconsistent state in the vendor dir and the user should fix it
if (!$prevPackage && is_dir($path) && !$this->filesystem->isDirEmpty($path)) {
throw new IrrecoverableDownloadException('Expected empty path to extract '.$package.' into but directory exists: '.$path);
}
return $res;
}
/**
* {@inheritDoc}
* @throws \RuntimeException
@ -40,7 +53,7 @@ abstract class ArchiveDownloader extends FileDownloader
$this->filesystem->ensureDirectoryExists($path);
if (!$this->filesystem->isDirEmpty($path)) {
throw new \RuntimeException('Expected empty path to extract '.$package.' into but directory exists: '.$path);
throw new \UnexpectedValueException('Expected empty path to extract '.$package.' into but directory exists: '.$path);
}
do {

View File

@ -15,6 +15,7 @@ namespace Composer\Downloader;
use Composer\Package\PackageInterface;
use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
use Composer\Exception\IrrecoverableDownloadException;
use React\Promise\PromiseInterface;
/**
@ -25,7 +26,6 @@ use React\Promise\PromiseInterface;
class DownloadManager
{
private $io;
private $httpDownloader;
private $preferDist = false;
private $preferSource = false;
private $packagePreferences = array();
@ -196,7 +196,7 @@ class DownloadManager
}
$handleError = function ($e) use ($sources, $source, $package, $io, $download) {
if ($e instanceof \RuntimeException) {
if ($e instanceof \RuntimeException && !$e instanceof IrrecoverableDownloadException) {
if (!$sources) {
throw $e;
}

View File

@ -141,7 +141,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
->then($accept, $reject);
}
return $result->then(function ($result) use ($fileName, $checksum, $url, $eventDispatcher) {
return $result->then(function ($result) use ($fileName, $checksum, $url, $package, $eventDispatcher) {
// 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.
@ -159,7 +159,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
}
if ($eventDispatcher) {
$postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, $fileName, $checksum, $url['processed']);
$postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, $fileName, $checksum, $url['processed'], $package);
$eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent);
}
@ -325,7 +325,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
*/
protected function getFileName(PackageInterface $package, $path)
{
return rtrim($this->config->get('vendor-dir').'/composer/'.md5($package.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.');
return rtrim($this->config->get('vendor-dir').'/composer/tmp-'.md5($package.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.');
}
/**

View File

@ -178,7 +178,7 @@ class SvnDownloader extends VcsDownloader
*/
protected function getCommitLogs($fromReference, $toReference, $path)
{
if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference)) {
if (preg_match('{@(\d+)$}', $fromReference) && preg_match('{@(\d+)$}', $toReference)) {
// retrieve the svn base url from the checkout folder
$command = sprintf('svn info --non-interactive --xml %s', ProcessExecutor::escape($path));
if (0 !== $this->process->execute($command, $output, $path)) {

View File

@ -160,7 +160,6 @@ class EventDispatcher
if (is_array($callable) && (is_string($callable[0]) || is_object($callable[0])) && is_string($callable[1])) {
$this->io->writeError(sprintf('> %s: %s', $event->getName(), (is_object($callable[0]) ? get_class($callable[0]) : $callable[0]).'->'.$callable[1] ), true, IOInterface::VERBOSE);
}
$event = $this->checkListenerExpectedEvent($callable, $event);
$return = false === call_user_func($callable, $event) ? 1 : 0;
} elseif ($this->isComposerScript($callable)) {
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE);
@ -304,8 +303,6 @@ class EventDispatcher
*/
protected function executeEventPhpScript($className, $methodName, Event $event)
{
$event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
if ($this->io->isVerbose()) {
$this->io->writeError(sprintf('> %s: %s::%s', $event->getName(), $className, $methodName));
} else {
@ -315,38 +312,6 @@ class EventDispatcher
return $className::$methodName($event);
}
/**
* @param mixed $target
* @param Event $event
* @return Event
*/
protected function checkListenerExpectedEvent($target, Event $event)
{
if (in_array($event->getName(), array(
'init',
'command',
'pre-file-download',
), true)) {
return $event;
}
try {
$reflected = new \ReflectionParameter($target, 0);
} catch (\Exception $e) {
return $event;
}
$typehint = $reflected->getClass();
if (!$typehint instanceof \ReflectionClass) {
return $event;
}
$expected = $typehint->getName();
return $event;
}
private function serializeCallback($cb)
{
if (is_array($cb) && count($cb) === 2) {

View File

@ -0,0 +1,20 @@
<?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\Exception;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class IrrecoverableDownloadException extends \RuntimeException
{
}

View File

@ -17,6 +17,7 @@ use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\Package\Archiver;
use Composer\Package\Version\VersionGuesser;
use Composer\Package\RootPackageInterface;
use Composer\Repository\RepositoryManager;
use Composer\Repository\RepositoryFactory;
use Composer\Repository\WritableRepositoryInterface;
@ -335,6 +336,7 @@ class Factory
$httpDownloader = self::createHttpDownloader($io, $config);
$loop = new Loop($httpDownloader);
$composer->setLoop($loop);
// initialize event dispatcher
$dispatcher = new EventDispatcher($composer, $io);
@ -344,9 +346,6 @@ class Factory
$rm = RepositoryFactory::manager($io, $config, $httpDownloader, $dispatcher);
$composer->setRepositoryManager($rm);
// load local repository
$this->addLocalRepository($io, $rm, $vendorDir);
// force-set the version of the global package if not defined as
// guessing it adds no value and only takes time
if (!$fullLoad && !isset($localConfig['version'])) {
@ -360,6 +359,9 @@ class Factory
$package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd);
$composer->setPackage($package);
// load local repository
$this->addLocalRepository($io, $rm, $vendorDir, $package);
// initialize installation manager
$im = $this->createInstallationManager($loop, $io, $dispatcher);
$composer->setInstallationManager($im);
@ -431,9 +433,9 @@ class Factory
* @param Repository\RepositoryManager $rm
* @param string $vendorDir
*/
protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir)
protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage)
{
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io)));
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io), true, $rootPackage));
}
/**
@ -582,7 +584,7 @@ class Factory
* @param array $options Array of options passed directly to HttpDownloader constructor
* @return HttpDownloader
*/
public static function createHttpDownloader(IOInterface $io, Config $config = null, $options = array())
public static function createHttpDownloader(IOInterface $io, Config $config, $options = array())
{
static $warned = false;
$disableTls = false;

View File

@ -135,7 +135,9 @@ abstract class BaseIO implements IOInterface
}
foreach ($gitlabToken as $domain => $token) {
$this->checkAndSetAuthentication($domain, $token, 'private-token');
$username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token;
$password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token';
$this->checkAndSetAuthentication($domain, $username, $password);
}
// reload http basic credentials from config if available

View File

@ -0,0 +1,186 @@
<?php
namespace Composer;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* To require it's presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
private static $installed;
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
return array_keys(self::$installed['versions']);
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @return bool
*/
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param ?string $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
*
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
$ranges = array();
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['version'])) {
return null;
}
return self::$installed['versions'][$packageName]['version'];
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return self::$installed['versions'][$packageName]['pretty_version'];
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
return null;
}
return self::$installed['versions'][$packageName]['reference'];
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}
*/
public static function getRootPackage()
{
return self::$installed['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>}
*/
public static function getRawData()
{
return self::$installed;
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
}
}

View File

@ -43,6 +43,7 @@ use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Version\VersionParser;
use Composer\Package\Package;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositorySet;
@ -128,6 +129,7 @@ class Installer
protected $dryRun = false;
protected $verbose = false;
protected $update = false;
protected $install = true;
protected $dumpAutoloader = true;
protected $runScripts = true;
protected $ignorePlatformReqs = false;
@ -202,6 +204,8 @@ class Installer
throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive.");
}
$isFreshInstall = $this->repositoryManager->getLocalRepository()->isFresh();
// Force update if there is no lock file present
if (!$this->update && !$this->locker->isLocked()) {
$this->io->writeError('<warning>No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.</warning>');
@ -217,6 +221,10 @@ class Installer
$this->mockLocalRepositories($this->repositoryManager);
}
if ($this->update && !$this->install) {
$this->dumpAutoloader = false;
}
if ($this->runScripts) {
$_SERVER['COMPOSER_DEV_MODE'] = (int) $this->devMode;
putenv('COMPOSER_DEV_MODE='.$_SERVER['COMPOSER_DEV_MODE']);
@ -238,8 +246,7 @@ class Installer
try {
if ($this->update) {
// TODO introduce option to set doInstall to false (update lock file without vendor install)
$res = $this->doUpdate($localRepo, true);
$res = $this->doUpdate($localRepo, $this->install);
} else {
$res = $this->doInstall($localRepo);
}
@ -247,13 +254,13 @@ class Installer
return $res;
}
} catch (\Exception $e) {
if ($this->executeOperations && $this->config->get('notify-on-install')) {
if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) {
$this->installationManager->notifyInstalls($this->io);
}
throw $e;
}
if ($this->executeOperations && $this->config->get('notify-on-install')) {
if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) {
$this->installationManager->notifyInstalls($this->io);
}
@ -263,6 +270,9 @@ class Installer
$this->createPlatformRepo(false),
new RootPackageRepository(clone $this->package),
));
if ($isFreshInstall) {
$this->suggestedPackagesReporter->addSuggestionsFromPackage($this->package);
}
$this->suggestedPackagesReporter->outputMinimalistic($installedRepo);
}
@ -298,10 +308,11 @@ class Installer
$this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative);
$this->autoloadGenerator->setApcu($this->apcuAutoloader);
$this->autoloadGenerator->setRunScripts($this->runScripts);
$this->autoloadGenerator->setIgnorePlatformRequirements($this->ignorePlatformReqs);
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
}
if ($this->executeOperations) {
if ($this->install && $this->executeOperations) {
// force binaries re-generation in case they are missing
foreach ($localRepo->getPackages() as $package) {
$this->installationManager->ensureBinariesPresence($package);
@ -402,7 +413,7 @@ class Installer
$solver = null;
} catch (SolverProblemsException $e) {
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool));
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
if (!$this->devMode) {
$this->io->writeError('<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>', true, IOInterface::QUIET);
}
@ -563,7 +574,7 @@ class Installer
$this->io->writeError('<error>Unable to find a compatible set of packages based on your non-dev requirements alone.</error>', true, IOInterface::QUIET);
$this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.');
$this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.');
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, true));
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true));
return max(1, $e->getCode());
}
@ -627,7 +638,7 @@ class Installer
}
} catch (SolverProblemsException $e) {
$this->io->writeError('<error>Your lock file does not contain a compatible set of packages. Please run composer update.</error>', true, IOInterface::QUIET);
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool));
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
return max(1, $e->getCode());
}
@ -727,7 +738,7 @@ class Installer
$rootRequires = array();
foreach ($requires as $req => $constraint) {
// skip platform requirements from the root package to avoid filtering out existing platform packages
if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
if ((true === $this->ignorePlatformReqs || (is_array($this->ignorePlatformReqs) && in_array($req, $this->ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
continue;
}
if ($constraint instanceof Link) {
@ -741,6 +752,8 @@ class Installer
$this->fixedRootPackage->setRequires(array());
$this->fixedRootPackage->setDevRequires(array());
$stabilityFlags[$this->package->getName()] = BasePackage::$stabilities[VersionParser::parseStability($this->package->getVersion())];
$repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $rootAliases, $this->package->getReferences(), $rootRequires);
$repositorySet->addRepository(new RootPackageRepository($this->fixedRootPackage));
$repositorySet->addRepository($platformRepo);
@ -1010,6 +1023,19 @@ class Installer
return $this;
}
/**
* Allows disabling the install step after an update
*
* @param bool $install
* @return Installer
*/
public function setInstall($install = true)
{
$this->install = (bool) $install;
return $this;
}
/**
* enables dev packages
*
@ -1092,12 +1118,22 @@ class Installer
/**
* set ignore Platform Package requirements
*
* @param bool $ignorePlatformReqs
* If this is set to true, all platform requirements are ignored
* If this is set to false, no platform requirements are ignored
* If this is set to string[], those packages will be ignored
*
* @param bool|array $ignorePlatformReqs
* @return Installer
*/
public function setIgnorePlatformRequirements($ignorePlatformReqs = false)
{
$this->ignorePlatformReqs = (bool) $ignorePlatformReqs;
if (is_array($ignorePlatformReqs)) {
$this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) {
return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req);
});
} else {
$this->ignorePlatformReqs = (bool) $ignorePlatformReqs;
}
return $this;
}

View File

@ -171,34 +171,98 @@ class InstallationManager
public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
{
$promises = array();
foreach ($operations as $operation) {
$opType = $operation->getOperationType();
$promise = null;
if ($opType === 'install') {
$package = $operation->getPackage();
$installer = $this->getInstaller($package->getType());
$promise = $installer->download($package);
} elseif ($opType === 'update') {
$target = $operation->getTargetPackage();
$targetType = $target->getType();
$installer = $this->getInstaller($targetType);
$promise = $installer->download($target, $operation->getInitialPackage());
}
if ($promise) {
$promises[] = $promise;
}
}
if (!empty($promises)) {
$this->loop->wait($promises);
}
$cleanupPromises = array();
$loop = $this->loop;
$runCleanup = function () use (&$cleanupPromises, $loop) {
$promises = array();
foreach ($cleanupPromises as $cleanup) {
$promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) {
$promise = $cleanup();
if (null === $promise) {
$resolve();
} else {
$promise->then(function () use ($resolve) {
$resolve();
});
}
});
}
if (!empty($promises)) {
$loop->wait($promises);
}
};
$handleInterruptsUnix = function_exists('pcntl_async_signals') && function_exists('pcntl_signal');
$handleInterruptsWindows = function_exists('sapi_windows_set_ctrl_handler');
$prevHandler = null;
$windowsHandler = null;
if ($handleInterruptsUnix) {
pcntl_async_signals(true);
$prevHandler = pcntl_signal_get_handler(SIGINT);
pcntl_signal(SIGINT, function ($sig) use ($runCleanup, $prevHandler) {
$runCleanup();
if (!in_array($prevHandler, array(SIG_DFL, SIG_IGN), true)) {
call_user_func($prevHandler, $sig);
}
exit(130);
});
}
if ($handleInterruptsWindows) {
$windowsHandler = function () use ($runCleanup) {
$runCleanup();
exit(130);
};
sapi_windows_set_ctrl_handler($windowsHandler, true);
}
try {
foreach ($operations as $operation) {
foreach ($operations as $index => $operation) {
$opType = $operation->getOperationType();
// ignoring alias ops as they don't need to execute anything at this stage
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
continue;
}
if ($opType === 'update') {
$package = $operation->getTargetPackage();
$initialPackage = $operation->getInitialPackage();
} else {
$package = $operation->getPackage();
$initialPackage = null;
}
$installer = $this->getInstaller($package->getType());
$cleanupPromises[$index] = function () use ($opType, $installer, $package, $initialPackage) {
// avoid calling cleanup if the download was not even initialized for a package
// as without installation source configured nothing will work
if (!$package->getInstallationSource()) {
return;
}
return $installer->cleanup($opType, $package, $initialPackage);
};
if ($opType !== 'uninstall') {
$promise = $installer->download($package, $initialPackage);
if ($promise) {
$promises[] = $promise;
}
}
}
// execute all downloads first
if (!empty($promises)) {
$this->loop->wait($promises);
}
foreach ($operations as $index => $operation) {
$opType = $operation->getOperationType();
// ignoring alias ops as they don't need to execute anything
@ -236,13 +300,9 @@ class InstallationManager
$promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); });
}
$cleanupPromise = function () use ($opType, $installer, $package, $initialPackage) {
return $installer->cleanup($opType, $package, $initialPackage);
};
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
return $installManager->$opType($repo, $operation);
})->then($cleanupPromise)
})->then($cleanupPromises[$index])
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
$repo->write($devMode, $installManager);
@ -256,34 +316,37 @@ class InstallationManager
throw $e;
});
$cleanupPromises[] = $cleanupPromise;
$promises[] = $promise;
}
// execute all prepare => installs/updates/removes => cleanup steps
if (!empty($promises)) {
$this->loop->wait($promises);
}
} catch (\Exception $e) {
$promises = array();
foreach ($cleanupPromises as $cleanup) {
$promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) {
$promise = $cleanup();
if (null === $promise) {
$resolve();
} else {
$promise->then(function () use ($resolve) {
$resolve();
});
}
});
}
$runCleanup();
if (!empty($promises)) {
$this->loop->wait($promises);
if ($handleInterruptsUnix) {
pcntl_signal(SIGINT, $prevHandler);
}
if ($handleInterruptsWindows) {
sapi_windows_set_ctrl_handler($prevHandler, false);
}
throw $e;
}
if ($handleInterruptsUnix) {
pcntl_signal(SIGINT, $prevHandler);
}
if ($handleInterruptsWindows) {
sapi_windows_set_ctrl_handler($prevHandler, false);
}
// do a last write so that we write the repository even if nothing changed
// as that can trigger an update of some files like InstalledVersions.php if
// running a new composer version
$repo->write($devMode, $this);
}
/**

View File

@ -14,7 +14,7 @@ namespace Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\InstalledRepository;
use Symfony\Component\Console\Formatter\OutputFormatter;
/**
@ -98,12 +98,14 @@ class SuggestedPackagesReporter
*
* Do not list the ones already installed if installed repository provided.
*
* @param int $mode One of the MODE_* constants from this class
* @param int $mode One of the MODE_* constants from this class
* @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped
* @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown
* @return SuggestedPackagesReporter
*/
public function output($mode, RepositoryInterface $installedRepo = null)
public function output($mode, InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null)
{
$suggestedPackages = $this->getFilteredSuggestions($installedRepo);
$suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf);
$suggesters = array();
$suggested = array();
@ -151,19 +153,27 @@ class SuggestedPackagesReporter
}
}
if ($onlyDependentsOf) {
$allSuggestedPackages = $this->getFilteredSuggestions($installedRepo);
$diff = count($allSuggestedPackages) - count($suggestedPackages);
if ($diff) {
$this->io->write('<info>'.$diff.' additional suggestions</info> by transitive dependencies can be shown with <info>--all</info>');
}
}
return $this;
}
/**
* Output number of new suggested packages and a hint to use suggest command.
**
* Do not list the ones already installed if installed repository provided.
*
* @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped
* @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown
* @return SuggestedPackagesReporter
*/
public function outputMinimalistic(RepositoryInterface $installedRepo = null)
public function outputMinimalistic(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null)
{
$suggestedPackages = $this->getFilteredSuggestions($installedRepo);
$suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf);
if ($suggestedPackages) {
$this->io->writeError('<info>'.count($suggestedPackages).' package suggestions were added by new dependencies, use `composer suggest` to see details.</info>');
}
@ -171,7 +181,12 @@ class SuggestedPackagesReporter
return $this;
}
private function getFilteredSuggestions(RepositoryInterface $installedRepo = null)
/**
* @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped
* @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown
* @return array[]
*/
private function getFilteredSuggestions(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null)
{
$suggestedPackages = $this->getPackages();
$installedNames = array();
@ -184,9 +199,17 @@ class SuggestedPackagesReporter
}
}
$sourceFilter = array();
if ($onlyDependentsOf) {
$sourceFilter = array_map(function ($link) {
return $link->getTarget();
}, array_merge($onlyDependentsOf->getRequires(), $onlyDependentsOf->getDevRequires()));
$sourceFilter[] = $onlyDependentsOf->getName();
}
$suggestions = array();
foreach ($suggestedPackages as $suggestion) {
if (in_array($suggestion['target'], $installedNames)) {
if (in_array($suggestion['target'], $installedNames) || ($sourceFilter && !in_array($suggestion['source'], $sourceFilter))) {
continue;
}

View File

@ -112,11 +112,17 @@ class JsonFile
*/
public function write(array $hash, $options = 448)
{
if ($this->path === 'php://memory') {
file_put_contents($this->path, static::encode($hash, $options));
return;
}
$dir = dirname($this->path);
if (!is_dir($dir)) {
if (file_exists($dir)) {
throw new \UnexpectedValueException(
$dir.' exists and is not a directory.'
realpath($dir).' exists and is not a directory.'
);
}
if (!@mkdir($dir, 0777, true)) {

View File

@ -25,6 +25,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
protected $dev;
protected $rootPackageAlias = false;
protected $stability;
protected $hasSelfVersionRequires = false;
/** @var PackageInterface */
protected $aliasOf;
@ -179,7 +180,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
$prettyVersion = $this->aliasOf->getPrettyVersion();
}
if (in_array($linkType, array('conflicts', 'provides', 'replaces'), true)) {
if (\in_array($linkType, array('conflicts', 'provides', 'replaces'), true)) {
$newLinks = array();
foreach ($links as $link) {
// link is self.version, but must be replacing also the replaced version
@ -192,6 +193,9 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
} else {
foreach ($links as $index => $link) {
if ('self.version' === $link->getPrettyConstraint()) {
if ($linkType === 'requires') {
$this->hasSelfVersionRequires = true;
}
$links[$index] = new Link($link->getSource(), $link->getTarget(), $constraint = new Constraint('=', $this->version), $linkType, $prettyVersion);
$constraint->setPrettyString($prettyVersion);
}
@ -201,6 +205,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
return $links;
}
public function hasSelfVersionRequires()
{
return $this->hasSelfVersionRequires;
}
/***************************************
* Wrappers around the aliased package *
***************************************/
@ -395,6 +404,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
return $this->aliasOf->getNotificationUrl();
}
public function getArchiveName()
{
return $this->aliasOf->getArchiveName();
}
public function getArchiveExcludes()
{
return $this->aliasOf->getArchiveExcludes();

View File

@ -78,7 +78,12 @@ class ArchiveManager
*/
public function getPackageFilename(PackageInterface $package)
{
$nameParts = array(preg_replace('#[^a-z0-9-_]#i', '-', $package->getName()));
if ($package->getArchiveName()) {
$baseName = $package->getArchiveName();
} else {
$baseName = preg_replace('#[^a-z0-9-_]#i', '-', $package->getName());
}
$nameParts = array($baseName);
if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) {
array_push($nameParts, $package->getDistReference(), $package->getDistType());
@ -131,20 +136,6 @@ class ArchiveManager
}
$filesystem = new Filesystem();
if (null === $fileName) {
$packageName = $this->getPackageFilename($package);
} else {
$packageName = $fileName;
}
// Archive filename
$filesystem->ensureDirectoryExists($targetDir);
$target = realpath($targetDir).'/'.$packageName.'.'.$format;
$filesystem->ensureDirectoryExists(dirname($target));
if (!$this->overwriteFiles && file_exists($target)) {
return $target;
}
if ($package instanceof RootPackageInterface) {
$sourcePath = realpath('.');
@ -167,12 +158,30 @@ class ArchiveManager
if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) {
$jsonFile = new JsonFile($composerJsonPath);
$jsonData = $jsonFile->read();
if (!empty($jsonData['archive']['name'])) {
$package->setArchiveName($jsonData['archive']['name']);
}
if (!empty($jsonData['archive']['exclude'])) {
$package->setArchiveExcludes($jsonData['archive']['exclude']);
}
}
}
if (null === $fileName) {
$packageName = $this->getPackageFilename($package);
} else {
$packageName = $fileName;
}
// Archive filename
$filesystem->ensureDirectoryExists($targetDir);
$target = realpath($targetDir).'/'.$packageName.'.'.$format;
$filesystem->ensureDirectoryExists(dirname($target));
if (!$this->overwriteFiles && file_exists($target)) {
return $target;
}
// Create the archive
$tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format;
$filesystem->ensureDirectoryExists(dirname($tempTarget));

View File

@ -215,7 +215,7 @@ abstract class BasePackage implements PackageInterface
public function getFullPrettyVersion($truncate = true, $displayMode = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV)
{
if ($displayMode === PackageInterface::DISPLAY_SOURCE_REF_IF_DEV &&
(!$this->isDev() || !in_array($this->getSourceType(), array('hg', 'git')))
(!$this->isDev() || !\in_array($this->getSourceType(), array('hg', 'git')))
) {
return $this->getPrettyVersion();
}
@ -233,7 +233,7 @@ abstract class BasePackage implements PackageInterface
}
// if source reference is a sha1 hash -- truncate
if ($truncate && strlen($reference) === 40) {
if ($truncate && \strlen($reference) === 40) {
return $this->getPrettyVersion() . ' ' . substr($reference, 0, 7);
}

View File

@ -213,6 +213,6 @@ class CompletePackage extends Package implements CompletePackageInterface
*/
public function getReplacementPackage()
{
return is_string($this->abandoned) ? $this->abandoned : null;
return \is_string($this->abandoned) ? $this->abandoned : null;
}
}

View File

@ -70,6 +70,9 @@ class ArrayDumper
}
}
if ($package->getArchiveName()) {
$data['archive']['name'] = $package->getArchiveName();
}
if ($package->getArchiveExcludes()) {
$data['archive']['exclude'] = $package->getArchiveExcludes();
}
@ -109,7 +112,7 @@ class ArrayDumper
$data = $this->dumpValues($package, $keys, $data);
if (isset($data['keywords']) && is_array($data['keywords'])) {
if (isset($data['keywords']) && \is_array($data['keywords'])) {
sort($data['keywords']);
}
@ -125,7 +128,7 @@ class ArrayDumper
}
}
if (count($package->getTransportOptions()) > 0) {
if (\count($package->getTransportOptions()) > 0) {
$data['transport-options'] = $package->getTransportOptions();
}
@ -142,7 +145,7 @@ class ArrayDumper
$getter = 'get'.ucfirst($method);
$value = $package->$getter();
if (null !== $value && !(is_array($value) && 0 === count($value))) {
if (null !== $value && !(\is_array($value) && 0 === \count($value))) {
$data[$key] = $value;
}
}

View File

@ -109,7 +109,7 @@ class ArrayLoader implements LoaderInterface
$package->setTargetDir($config['target-dir']);
}
if (isset($config['extra']) && is_array($config['extra'])) {
if (isset($config['extra']) && \is_array($config['extra'])) {
$package->setExtra($config['extra']);
}
@ -159,7 +159,7 @@ class ArrayLoader implements LoaderInterface
}
}
if (isset($config['suggest']) && is_array($config['suggest'])) {
if (isset($config['suggest']) && \is_array($config['suggest'])) {
foreach ($config['suggest'] as $target => $reason) {
if ('self.version' === trim($reason)) {
$config['suggest'][$target] = $package->getPrettyVersion();
@ -194,12 +194,15 @@ class ArrayLoader implements LoaderInterface
$package->setNotificationUrl($config['notification-url']);
}
if (!empty($config['archive']['name'])) {
$package->setArchiveName($config['archive']['name']);
}
if (!empty($config['archive']['exclude'])) {
$package->setArchiveExcludes($config['archive']['exclude']);
}
if ($package instanceof Package\CompletePackageInterface) {
if (isset($config['scripts']) && is_array($config['scripts'])) {
if (isset($config['scripts']) && \is_array($config['scripts'])) {
foreach ($config['scripts'] as $event => $listeners) {
$config['scripts'][$event] = (array) $listeners;
}
@ -209,23 +212,23 @@ class ArrayLoader implements LoaderInterface
$package->setScripts($config['scripts']);
}
if (!empty($config['description']) && is_string($config['description'])) {
if (!empty($config['description']) && \is_string($config['description'])) {
$package->setDescription($config['description']);
}
if (!empty($config['homepage']) && is_string($config['homepage'])) {
if (!empty($config['homepage']) && \is_string($config['homepage'])) {
$package->setHomepage($config['homepage']);
}
if (!empty($config['keywords']) && is_array($config['keywords'])) {
if (!empty($config['keywords']) && \is_array($config['keywords'])) {
$package->setKeywords($config['keywords']);
}
if (!empty($config['license'])) {
$package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license']));
$package->setLicense(\is_array($config['license']) ? $config['license'] : array($config['license']));
}
if (!empty($config['authors']) && is_array($config['authors'])) {
if (!empty($config['authors']) && \is_array($config['authors'])) {
$package->setAuthors($config['authors']);
}
@ -233,7 +236,7 @@ class ArrayLoader implements LoaderInterface
$package->setSupport($config['support']);
}
if (!empty($config['funding']) && is_array($config['funding'])) {
if (!empty($config['funding']) && \is_array($config['funding'])) {
$package->setFunding($config['funding']);
}
@ -307,8 +310,8 @@ class ArrayLoader implements LoaderInterface
private function createLink($source, $sourceVersion, $description, $target, $prettyConstraint)
{
if (!is_string($prettyConstraint)) {
throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')');
if (!\is_string($prettyConstraint)) {
throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.\gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')');
}
if ('self.version' === $prettyConstraint) {
$parsedConstraint = $this->versionParser->parseConstraints($sourceVersion);
@ -331,7 +334,7 @@ class ArrayLoader implements LoaderInterface
return;
}
if (isset($config['extra']['branch-alias']) && is_array($config['extra']['branch-alias'])) {
if (isset($config['extra']['branch-alias']) && \is_array($config['extra']['branch-alias'])) {
foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) {
// ensure it is an alias to a -dev package
if ('-dev' !== substr($targetBranch, -4)) {
@ -361,7 +364,7 @@ class ArrayLoader implements LoaderInterface
}
}
if (in_array($config['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) {
if (\in_array($config['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) {
return VersionParser::DEV_MASTER_ALIAS;
}
}

View File

@ -57,6 +57,7 @@ class Package extends BasePackage
protected $autoload = array();
protected $devAutoload = array();
protected $includePaths = array();
protected $archiveName;
protected $archiveExcludes = array();
/**
@ -551,6 +552,24 @@ class Package extends BasePackage
return $this->notificationUrl;
}
/**
* Sets default base filename for archive
*
* @param string $name
*/
public function setArchiveName($name)
{
$this->archiveName = $name;
}
/**
* {@inheritDoc}
*/
public function getArchiveName()
{
return $this->archiveName;
}
/**
* Sets a list of patterns to be excluded from archives
*
@ -619,7 +638,7 @@ class Package extends BasePackage
} else {
continue;
}
if (!in_array($mirrorUrl, $urls)) {
if (!\in_array($mirrorUrl, $urls)) {
$func = $mirror['preferred'] ? 'array_unshift' : 'array_push';
$func($urls, $mirrorUrl);
}

View File

@ -283,7 +283,7 @@ interface PackageInterface
* directories for autoloading using the type specified.
*
* @return array Mapping of autoloading rules
* @psalm-return array{psr-0?: array<string, string>, psr-4?: array<string, string>, classmap?: list<string>, file?: list<string>}
* @psalm-return array{psr-0?: array<string, string>, psr-4?: array<string, string>, classmap?: list<string>, files?: list<string>}
*/
public function getAutoload();
@ -296,7 +296,7 @@ interface PackageInterface
* directories for autoloading using the type specified.
*
* @return array Mapping of dev autoloading rules
* @psalm-return array{psr-0?: array<string, string>, psr-4?: array<string, string>, classmap?: list<string>, file?: list<string>}
* @psalm-return array{psr-0?: array<string, string>, psr-4?: array<string, string>, classmap?: list<string>, files?: list<string>}
*/
public function getDevAutoload();
@ -357,6 +357,13 @@ interface PackageInterface
*/
public function getPrettyString();
/**
* Returns default base filename for archive
*
* @return array
*/
public function getArchiveName();
/**
* Returns a list of patterns to exclude from package archives
*

View File

@ -15,9 +15,12 @@ namespace Composer\Package\Version;
use Composer\DependencyResolver\Pool;
use Composer\Package\BasePackage;
use Composer\Package\PackageInterface;
use Composer\Plugin\PluginInterface;
use Composer\Composer;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Repository\RepositorySet;
use Composer\Repository\PlatformRepository;
use Composer\Semver\Constraint\Constraint;
/**
@ -30,11 +33,21 @@ class VersionSelector
{
private $repositorySet;
private $platformConstraints = array();
private $parser;
public function __construct(RepositorySet $repositorySet)
/**
* @param PlatformRepository $platformRepo If passed in, the versions found will be filtered against their requirements to eliminate any not matching the current platform packages
*/
public function __construct(RepositorySet $repositorySet, PlatformRepository $platformRepo = null)
{
$this->repositorySet = $repositorySet;
if ($platformRepo) {
foreach ($platformRepo->getPackages() as $package) {
$this->platformConstraints[$package->getName()][] = new Constraint('==', $package->getVersion());
}
}
}
/**
@ -43,21 +56,39 @@ class VersionSelector
*
* @param string $packageName
* @param string $targetPackageVersion
* @param string $targetPhpVersion
* @param string $preferredStability
* @param bool|array $ignorePlatformReqs
* @return PackageInterface|false
*/
public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable')
public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false)
{
if (!isset(BasePackage::$stabilities[$preferredStability])) {
// If you get this, maybe you are still relying on the Composer 1.x signature where the 3rd arg was the php version
throw new \UnexpectedValueException('Expected a valid stability name as 3rd argument, got '.$preferredStability);
}
$constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
$candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint);
if ($targetPhpVersion) {
$phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion));
$candidates = array_filter($candidates, function ($pkg) use ($phpConstraint) {
if ($this->platformConstraints && true !== $ignorePlatformReqs) {
$platformConstraints = $this->platformConstraints;
$ignorePlatformReqs = $ignorePlatformReqs ?: array();
$candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $ignorePlatformReqs) {
$reqs = $pkg->getRequires();
return !isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint);
foreach ($reqs as $name => $link) {
if (!in_array($name, $ignorePlatformReqs, true) && isset($platformConstraints[$name])) {
foreach ($platformConstraints[$name] as $constraint) {
if ($link->getConstraint()->matches($constraint)) {
continue 2;
}
}
return false;
}
}
return true;
});
}

View File

@ -25,6 +25,11 @@ interface PluginInterface
/**
* Version number of the internal composer-plugin-api package
*
* This is used to denote the API version of Plugin specific
* features, but is also bumped to a new major if Composer
* includes a major break in internal APIs which are susceptible
* to be used by plugins.
*
* @var string
*/
const PLUGIN_API_VERSION = '2.0.0';

View File

@ -134,8 +134,8 @@ class PluginManager
$currentPluginApiVersion = $this->getPluginApiVersion();
$currentPluginApiConstraint = new Constraint('==', $this->versionParser->normalize($currentPluginApiVersion));
if ($requiresComposer->getPrettyString() === '1.0.0' && $this->getPluginApiVersion() === '1.0.0') {
$this->io->writeError('<warning>The "' . $package->getName() . '" plugin requires composer-plugin-api 1.0.0, this *WILL* break in the future and it should be fixed ASAP (require ^1.0 for example).</warning>');
if ($requiresComposer->getPrettyString() === $this->getPluginApiVersion()) {
$this->io->writeError('<warning>The "' . $package->getName() . '" plugin requires composer-plugin-api '.$this->getPluginApiVersion().', this *WILL* break in the future and it should be fixed ASAP (require ^'.$this->getPluginApiVersion().' instead for example).</warning>');
} elseif (!$requiresComposer->matches($currentPluginApiConstraint)) {
$this->io->writeError('<warning>The "' . $package->getName() . '" plugin was skipped because it requires a Plugin API version ("' . $requiresComposer->getPrettyString() . '") that does not match your Composer installation ("' . $currentPluginApiVersion . '"). You may need to run composer update with the "--no-plugins" option.</warning>');
@ -347,6 +347,8 @@ class PluginManager
/**
* Load all plugins and installers from a repository
*
* If a plugin requires another plugin, the required one will be loaded first
*
* Note that plugins in the specified repository that rely on events that
* have fired prior to loading will be missed. This means you likely want to
* call this method as early as possible.
@ -358,7 +360,7 @@ class PluginManager
private function loadRepository(RepositoryInterface $repo)
{
$packages = $repo->getPackages();
$sortedPackages = array_reverse(PackageSorter::sortPackages($packages));
$sortedPackages = PackageSorter::sortPackages($packages);
foreach ($sortedPackages as $package) {
if (!($package instanceof CompletePackage)) {
continue;

View File

@ -13,7 +13,7 @@
namespace Composer\Plugin;
use Composer\EventDispatcher\Event;
use Composer\Util\RemoteFilesystem;
use Composer\Package\PackageInterface;
/**
* The post file download event.
@ -38,6 +38,11 @@ class PostFileDownloadEvent extends Event
*/
private $url;
/**
* @var \Composer\Package\PackageInterface
*/
private $package;
/**
* Constructor.
*
@ -45,13 +50,15 @@ class PostFileDownloadEvent extends Event
* @param string $fileName The file name
* @param string|null $checksum The checksum
* @param string $url The processed url
* @param PackageInterface $package The package.
*/
public function __construct($name, $fileName, $checksum, $url)
public function __construct($name, $fileName, $checksum, $url, PackageInterface $package)
{
parent::__construct($name);
$this->fileName = $fileName;
$this->checksum = $checksum;
$this->url = $url;
$this->package = $package;
}
/**
@ -82,4 +89,14 @@ class PostFileDownloadEvent extends Event
return $this->url;
}
/**
* Get the package.
*
* @return \Composer\Package\PackageInterface
* The package.
*/
public function getPackage() {
return $this->package;
}
}

View File

@ -31,7 +31,7 @@ use Composer\EventDispatcher\EventDispatcher;
use Composer\Downloader\TransportException;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\EmptyConstraint;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Util\Http\Response;
use Composer\Util\MetadataMinifier;
use Composer\Util\Url;
@ -70,6 +70,14 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
private $hasPartialPackages;
private $partialPackagesByName;
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
* @var array list of package names which are fresh and can be loaded from the cache directly in case loadPackage is called several times
* useful for v2 metadata repositories with lazy providers
*/
public $freshMetadataUrls = array();
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
@ -257,7 +265,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
if (is_array($this->availablePackages)) {
$packageMap = array();
foreach ($this->availablePackages as $name) {
$packageMap[$name] = new EmptyConstraint();
$packageMap[$name] = new MatchAllConstraint();
}
$result = $this->loadAsyncPackages($packageMap);
@ -363,9 +371,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
if ($this->lazyProvidersUrl && count($packageNameMap)) {
if (is_array($this->availablePackages)) {
$availPackages = $this->availablePackages;
$packageNameMap = array_filter($packageNameMap, function ($name) use ($availPackages) {
return isset($availPackages[strtolower($name)]);
}, ARRAY_FILTER_USE_KEY);
foreach ($packageNameMap as $name => $constraint) {
if (!isset($availPackages[strtolower($name)])) {
unset($packageNameMap[$name]);
}
}
}
$result = $this->loadAsyncPackages($packageNameMap, $acceptableStabilities, $stabilityFlags);
@ -502,7 +512,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
{
if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) {
// skip platform packages, root package and composer-plugin-api
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name || 'composer-plugin-api' === $name) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name) {
return array();
}
@ -662,7 +672,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
// load ~dev versions of the packages as well if needed
foreach ($packageNames as $name => $constraint) {
if ($acceptableStabilities && $stabilityFlags && StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) {
if ($acceptableStabilities === null || $stabilityFlags === null || StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) {
$packageNames[$name.'~dev'] = $constraint;
}
}
@ -672,7 +682,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$realName = preg_replace('{~dev$}', '', $name);
// skip platform packages, root package and composer-plugin-api
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $realName) || '__root__' === $realName || 'composer-plugin-api' === $realName) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $realName) || '__root__' === $realName) {
continue;
}
@ -818,7 +828,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$this->hasPartialPackages = !empty($data['packages']) && is_array($data['packages']);
}
// metadata-url indiates V2 repo protocol so it takes over from all the V1 types
// metadata-url indicates V2 repo protocol so it takes over from all the V1 types
// V2 only has lazyProviders and possibly partial packages, but no ability to process anything else,
// V2 also supports async loading
if (!empty($data['metadata-url'])) {
@ -995,7 +1005,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
// url-encode $ signs in URLs as bad proxies choke on them
if (($pos = strpos($filename, '$')) && preg_match('{^https?://.*}i', $filename)) {
if (($pos = strpos($filename, '$')) && preg_match('{^https?://}i', $filename)) {
$filename = substr($filename, 0, $pos) . '%24' . substr($filename, $pos + 1);
}
@ -1148,6 +1158,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return new Promise(function ($resolve, $reject) { $resolve(array('packages' => array())); });
}
if (isset($this->freshMetadataUrls[$filename]) && $lastModifiedTime) {
// make it look like we got a 304 response
return new Promise(function ($resolve, $reject) { $resolve(true); });
}
$httpDownloader = $this->httpDownloader;
if ($this->eventDispatcher) {
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename);
@ -1171,6 +1186,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$json = $response->getBody();
if ($json === '' && $response->getStatusCode() === 304) {
$repo->freshMetadataUrls[$filename] = true;
return true;
}
@ -1184,6 +1200,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE);
}
$cache->write($cacheKey, $json);
$repo->freshMetadataUrls[$filename] = true;
return $data;
};

View File

@ -14,6 +14,8 @@ namespace Composer\Repository;
use Composer\Json\JsonFile;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\RootPackageInterface;
use Composer\Package\AliasPackage;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Installer\InstallationManager;
use Composer\Util\Filesystem;
@ -26,17 +28,26 @@ use Composer\Util\Filesystem;
*/
class FilesystemRepository extends WritableArrayRepository
{
private $file;
protected $file;
private $dumpVersions;
private $rootPackage;
/**
* Initializes filesystem repository.
*
* @param JsonFile $repositoryFile repository json file
* @param bool $dumpVersions
* @param ?RootPackageInterface $rootPackage Must be provided if $dumpVersions is true
*/
public function __construct(JsonFile $repositoryFile)
public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null)
{
parent::__construct();
$this->file = $repositoryFile;
$this->dumpVersions = $dumpVersions;
$this->rootPackage = $rootPackage;
if ($dumpVersions && !$rootPackage) {
throw new \InvalidArgumentException('Expected a root package instance if $dumpVersions is true');
}
}
/**
@ -105,5 +116,90 @@ class FilesystemRepository extends WritableArrayRepository
});
$this->file->write($data);
if ($this->dumpVersions) {
$versions = array('versions' => array());
$packages = $this->getPackages();
$packages[] = $rootPackage = $this->rootPackage;
while ($rootPackage instanceof AliasPackage) {
$rootPackage = $rootPackage->getAliasOf();
$packages[] = $rootPackage;
}
// add real installed packages
foreach ($packages as $package) {
if ($package instanceof AliasPackage) {
continue;
}
$reference = null;
if ($package->getInstallationSource()) {
$reference = $package->getInstallationSource() === 'source' ? $package->getSourceReference() : $package->getDistReference();
}
if (null === $reference) {
$reference = ($package->getSourceReference() ?: $package->getDistReference()) ?: null;
}
$versions['versions'][$package->getName()] = array(
'pretty_version' => $package->getPrettyVersion(),
'version' => $package->getVersion(),
'aliases' => array(),
'reference' => $reference,
);
if ($package instanceof RootPackageInterface) {
$versions['root'] = $versions['versions'][$package->getName()];
$versions['root']['name'] = $package->getName();
}
}
// add provided/replaced packages
foreach ($packages as $package) {
foreach ($package->getReplaces() as $replace) {
// exclude platform replaces as when they are really there we can not check for their presence
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $replace->getTarget())) {
continue;
}
$replaced = $replace->getPrettyConstraint();
if ($replaced === 'self.version') {
$replaced = $package->getPrettyVersion();
}
if (!isset($versions['versions'][$replace->getTarget()]['replaced']) || !in_array($replaced, $versions['versions'][$replace->getTarget()]['replaced'], true)) {
$versions['versions'][$replace->getTarget()]['replaced'][] = $replaced;
}
}
foreach ($package->getProvides() as $provide) {
// exclude platform provides as when they are really there we can not check for their presence
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $provide->getTarget())) {
continue;
}
$provided = $provide->getPrettyConstraint();
if ($provided === 'self.version') {
$provided = $package->getPrettyVersion();
}
if (!isset($versions['versions'][$provide->getTarget()]['provided']) || !in_array($provided, $versions['versions'][$provide->getTarget()]['provided'], true)) {
$versions['versions'][$provide->getTarget()]['provided'][] = $provided;
}
}
}
// add aliases
foreach ($packages as $package) {
if (!$package instanceof AliasPackage) {
continue;
}
$versions['versions'][$package->getName()]['aliases'][] = $package->getPrettyVersion();
if ($package instanceof RootPackageInterface) {
$versions['root']['aliases'][] = $package->getPrettyVersion();
}
}
ksort($versions['versions']);
ksort($versions);
$fs->filePutContentsIfModified($repoDir.'/installed.php', '<?php return '.var_export($versions, true).';'."\n");
$installedVersionsClass = file_get_contents(__DIR__.'/../InstalledVersions.php');
$installedVersionsClass = str_replace('private static $installed;', 'private static $installed = '.var_export($versions, true).';', $installedVersionsClass);
$fs->filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass);
}
}
}

View File

@ -25,4 +25,14 @@ class InstalledArrayRepository extends WritableArrayRepository implements Instal
{
return 'installed '.parent::getRepoName();
}
/**
* {@inheritDoc}
*/
public function isFresh()
{
// this is not a completely correct implementation but there is no way to
// distinguish an empty repo and a newly created one given this is all in-memory
return $this->count() === 0;
}
}

View File

@ -23,4 +23,12 @@ class InstalledFilesystemRepository extends FilesystemRepository implements Inst
{
return 'installed '.parent::getRepoName();
}
/**
* {@inheritDoc}
*/
public function isFresh()
{
return !$this->file->exists();
}
}

View File

@ -21,4 +21,8 @@ namespace Composer\Repository;
*/
interface InstalledRepositoryInterface extends WritableRepositoryInterface
{
/**
* @return bool true if packages were never installed in this repository
*/
public function isFresh();
}

View File

@ -12,6 +12,7 @@
namespace Composer\Repository;
use Composer\Composer;
use Composer\Package\CompletePackage;
use Composer\Package\PackageInterface;
use Composer\Package\Version\VersionParser;
@ -27,7 +28,7 @@ use Symfony\Component\Process\ExecutableFinder;
*/
class PlatformRepository extends ArrayRepository
{
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-plugin-api)$}iD';
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD';
private $versionParser;
@ -79,6 +80,12 @@ class PlatformRepository extends ArrayRepository
$composerPluginApi->setDescription('The Composer Plugin API');
$this->addPackage($composerPluginApi);
$prettyVersion = Composer::RUNTIME_API_VERSION;
$version = $this->versionParser->normalize($prettyVersion);
$composerRuntimeApi = new CompletePackage('composer-runtime-api', $version, $prettyVersion);
$composerRuntimeApi->setDescription('The Composer Runtime API');
$this->addPackage($composerRuntimeApi);
try {
$prettyVersion = PHP_VERSION;
$version = $this->versionParser->normalize($prettyVersion);

View File

@ -163,9 +163,9 @@ class RepositoryManager
/**
* Sets local repository for the project.
*
* @param WritableRepositoryInterface $repository repository instance
* @param InstalledRepositoryInterface $repository repository instance
*/
public function setLocalRepository(WritableRepositoryInterface $repository)
public function setLocalRepository(InstalledRepositoryInterface $repository)
{
$this->localRepository = $repository;
}
@ -173,7 +173,7 @@ class RepositoryManager
/**
* Returns local repository for the project.
*
* @return WritableRepositoryInterface
* @return InstalledRepositoryInterface
*/
public function getLocalRepository()
{

View File

@ -28,6 +28,8 @@ use Composer\Spdx\SpdxLicenses;
*/
class ConfigValidator
{
const CHECK_VERSION = 1;
private $io;
public function __construct(IOInterface $io)
@ -40,10 +42,11 @@ class ConfigValidator
*
* @param string $file The path to the file
* @param int $arrayLoaderValidationFlags Flags for ArrayLoader validation
* @param int $flags Flags for validation
*
* @return array a triple containing the errors, publishable errors, and warnings
*/
public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL)
public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL, $flags = self::CHECK_VERSION)
{
$errors = array();
$publishErrors = array();
@ -109,7 +112,7 @@ class ConfigValidator
}
}
if (isset($manifest['version'])) {
if (($flags & self::CHECK_VERSION) && isset($manifest['version'])) {
$warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.';
}

View File

@ -58,7 +58,7 @@ class Filesystem
->depth(0)
->in($dir);
return count($finder) === 0;
return \count($finder) === 0;
}
public function emptyDirectory($dir, $ensureDirectoryExists = true)
@ -116,7 +116,7 @@ class Filesystem
throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.');
}
if (!function_exists('proc_open')) {
if (!\function_exists('proc_open')) {
return $this->removeDirectoryPhp($directory);
}
@ -311,9 +311,9 @@ class Filesystem
return;
}
if (!function_exists('proc_open')) {
if (!\function_exists('proc_open')) {
$this->copyThenRemove($source, $target);
return;
}
@ -369,13 +369,13 @@ class Filesystem
$from = rtrim($from, '/') . '/dummy_file';
}
if (dirname($from) === dirname($to)) {
if (\dirname($from) === \dirname($to)) {
return './'.basename($to);
}
$commonPath = $to;
while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) {
$commonPath = strtr(dirname($commonPath), '\\', '/');
$commonPath = strtr(\dirname($commonPath), '\\', '/');
}
if (0 !== strpos($from, $commonPath) || '/' === $commonPath) {
@ -383,10 +383,10 @@ class Filesystem
}
$commonPath = rtrim($commonPath, '/') . '/';
$sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/');
$sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/');
$commonPathCode = str_repeat('../', $sourcePathDepth);
return ($commonPathCode . substr($to, strlen($commonPath))) ?: './';
return ($commonPathCode . substr($to, \strlen($commonPath))) ?: './';
}
/**
@ -414,7 +414,7 @@ class Filesystem
$commonPath = $to;
while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) {
$commonPath = strtr(dirname($commonPath), '\\', '/');
$commonPath = strtr(\dirname($commonPath), '\\', '/');
}
if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) {
@ -423,17 +423,17 @@ class Filesystem
$commonPath = rtrim($commonPath, '/') . '/';
if (strpos($to, $from.'/') === 0) {
return '__DIR__ . '.var_export(substr($to, strlen($from)), true);
return '__DIR__ . '.var_export(substr($to, \strlen($from)), true);
}
$sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories;
$sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/') + $directories;
if ($staticCode) {
$commonPathCode = "__DIR__ . '".str_repeat('/..', $sourcePathDepth)."'";
} else {
$commonPathCode = str_repeat('dirname(', $sourcePathDepth).'__DIR__'.str_repeat(')', $sourcePathDepth);
}
$relTarget = substr($to, strlen($commonPath));
$relTarget = substr($to, \strlen($commonPath));
return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : '');
return $commonPathCode . (\strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : '');
}
/**
@ -484,7 +484,7 @@ class Filesystem
// extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive:
if (preg_match('{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix', $path, $match)) {
$prefix = $match[1];
$path = substr($path, strlen($prefix));
$path = substr($path, \strlen($prefix));
}
if (substr($path, 0, 1) === '/') {
@ -579,7 +579,7 @@ class Filesystem
$cwd = getcwd();
$relativePath = $this->findShortestPath($link, $target);
chdir(dirname($link));
chdir(\dirname($link));
$result = @symlink($relativePath, $link);
chdir($cwd);
@ -632,7 +632,7 @@ class Filesystem
$resolved = rtrim($pathname, '/');
if (!strlen($resolved)) {
if (!\strlen($resolved)) {
return $pathname;
}
@ -721,4 +721,61 @@ class Filesystem
return $this->rmdir($junction);
}
public function filePutContentsIfModified($path, $content)
{
$currentContent = @file_get_contents($path);
if (!$currentContent || ($currentContent != $content)) {
return file_put_contents($path, $content);
}
return 0;
}
/**
* Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463
*
* @param string $source
* @param string $target
*/
public function safeCopy($source, $target)
{
if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) {
$source = fopen($source, 'r');
$target = fopen($target, 'w+');
stream_copy_to_stream($source, $target);
fclose($source);
fclose($target);
}
}
/**
* compare 2 files
* https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files
*/
private function filesAreEqual($a, $b)
{
// Check if filesize is different
if (filesize($a) !== filesize($b)) {
return false;
}
// Check if content is different
$ah = fopen($a, 'rb');
$bh = fopen($b, 'rb');
$result = true;
while (!feof($ah)) {
if (fread($ah, 8192) != fread($bh, 8192)) {
$result = false;
break;
}
}
fclose($ah);
fclose($bh);
return $result;
}
}

View File

@ -165,7 +165,7 @@ class Git
$errorMsg = $this->process->getErrorOutput();
}
} elseif (
preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match)
preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match)
|| preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match)
) {
if ($match[1] === 'git') {

View File

@ -71,17 +71,28 @@ class GitLab
return true;
}
// if available use token from composer config
$authTokens = $this->config->get('gitlab-token');
if (isset($authTokens[$originUrl])) {
$this->io->setAuthentication($originUrl, $authTokens[$originUrl], 'private-token');
// if available use deploy token from git config
if (0 === $this->process->execute('git config gitlab.deploytoken.user', $tokenUser) && 0 === $this->process->execute('git config gitlab.deploytoken.token', $tokenPassword)) {
$this->io->setAuthentication($originUrl, trim($tokenUser), trim($tokenPassword));
return true;
}
// if available use token from composer config
$authTokens = $this->config->get('gitlab-token');
if (isset($authTokens[$originUrl])) {
$token = $authTokens[$originUrl];
}
if (isset($authTokens[$bcOriginUrl])) {
$this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token');
$token = $authTokens[$bcOriginUrl];
}
if(isset($token)){
$username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token;
$password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token';
$this->io->setAuthentication($originUrl, $username, $password);
return true;
}

View File

@ -75,7 +75,7 @@ class CurlDownloader
$this->multiHandle = $mh = curl_multi_init();
if (function_exists('curl_multi_setopt')) {
curl_multi_setopt($mh, CURLMOPT_PIPELINING, PHP_VERSION_ID >= 70400 ? /* CURLPIPE_MULTIPLEX */ 2 : /*CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX*/ 3);
if (defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
if (defined('CURLMOPT_MAX_HOST_CONNECTIONS') && !defined('HHVM_VERSION')) {
curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 8);
}
}

View File

@ -55,9 +55,9 @@ class PackageSorter
$weightList = array();
foreach ($packages as $name => $package) {
$weight = $computeImportance($name);
$weightList[$name] = $weight;
foreach ($packages as $index => $package) {
$weight = $computeImportance($package->getName());
$weightList[$index] = $weight;
}
$stable_sort = function (&$array) {
@ -84,8 +84,8 @@ class PackageSorter
$sortedPackages = array();
foreach (array_keys($weightList) as $name) {
$sortedPackages[] = $packages[$name];
foreach (array_keys($weightList) as $index) {
$sortedPackages[] = $packages[$index];
}
return $sortedPackages;
}

View File

@ -55,7 +55,7 @@ class Platform
return $home;
}
if (function_exists('posix_getuid') && function_exists('posix_getpwuid')) {
if (\function_exists('posix_getuid') && \function_exists('posix_getpwuid')) {
$info = posix_getpwuid(posix_getuid());
return $info['dir'];
@ -69,7 +69,7 @@ class Platform
*/
public static function isWindows()
{
return defined('PHP_WINDOWS_VERSION_BUILD');
return \defined('PHP_WINDOWS_VERSION_BUILD');
}
/**
@ -80,13 +80,13 @@ class Platform
{
static $useMbString = null;
if (null === $useMbString) {
$useMbString = function_exists('mb_strlen') && ini_get('mbstring.func_overload');
$useMbString = \function_exists('mb_strlen') && ini_get('mbstring.func_overload');
}
if ($useMbString) {
return mb_strlen($str, '8bit');
}
return strlen($str);
return \strlen($str);
}
}

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