commit
d4e5135d0f
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
93
.travis.yml
93
.travis.yml
|
@ -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'
|
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
--------------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
42
appveyor.yml
42
appveyor.yml
|
@ -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
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
← [Repositories](05-repositories.md) | [Community](07-community.md) →
|
||||
## 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.
|
||||
|
||||
← [Repositories](05-repositories.md) | [Runtime](07-runtime.md) →
|
||||
|
|
|
@ -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`.
|
||||
|
||||
← [Config](06-config.md) | [Community](08-community.md) →
|
|
@ -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).
|
||||
|
||||
← [Config](06-config.md)
|
||||
← [Config](07-runtime.md)
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/../src/bootstrap.php';
|
|
@ -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\(\)\.$~'
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
failOnRisky="true"
|
||||
strict="false"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>');
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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)
|
||||
;
|
||||
|
|
|
@ -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'))
|
||||
;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -64,7 +64,7 @@ class GenericRule extends Rule
|
|||
|
||||
public function isAssertion()
|
||||
{
|
||||
return 1 === count($this->literals);
|
||||
return 1 === \count($this->literals);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -76,7 +76,7 @@ class Rule2Literals extends Rule
|
|||
}
|
||||
|
||||
$literals = $rule->getLiterals();
|
||||
if (2 != count($literals)) {
|
||||
if (2 != \count($literals)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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), '.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,12 @@ class InstalledFilesystemRepository extends FilesystemRepository implements Inst
|
|||
{
|
||||
return 'installed '.parent::getRepoName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isFresh()
|
||||
{
|
||||
return !$this->file->exists();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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.';
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue