Merge remote-tracking branch 'composer/master' into gitlab
Conflicts: doc/04-schema.md src/Composer/Util/RemoteFilesystem.phppull/3765/head
commit
ac68a721f4
|
@ -0,0 +1,8 @@
|
||||||
|
# Auto-detect text files, ensure they use LF.
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# These files are always considered text and should use LF.
|
||||||
|
# See core.whitespace @ http://git-scm.com/docs/git-config for whitespace flags.
|
||||||
|
*.php text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 diff=php
|
||||||
|
*.json text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4
|
||||||
|
*.test text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4
|
|
@ -8,3 +8,4 @@ phpunit.xml
|
||||||
.vagrant
|
.vagrant
|
||||||
Vagrantfile
|
Vagrantfile
|
||||||
.idea
|
.idea
|
||||||
|
.php_cs.cache
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$header = <<<EOF
|
||||||
|
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.
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$finder = Symfony\CS\Finder\DefaultFinder::create()
|
||||||
|
->files()
|
||||||
|
->name('*.php')
|
||||||
|
->exclude('Fixtures')
|
||||||
|
->in(__DIR__.'/src')
|
||||||
|
->in(__DIR__.'/tests')
|
||||||
|
;
|
||||||
|
|
||||||
|
return Symfony\CS\Config\Config::create()
|
||||||
|
->setUsingCache(true)
|
||||||
|
->setRiskyAllowed(true)
|
||||||
|
->setRules(array(
|
||||||
|
'@PSR2' => true,
|
||||||
|
'duplicate_semicolon' => true,
|
||||||
|
'extra_empty_lines' => true,
|
||||||
|
'header_comment' => array('header' => $header),
|
||||||
|
'include' => true,
|
||||||
|
'long_array_syntax' => true,
|
||||||
|
'method_separation' => true,
|
||||||
|
'multiline_array_trailing_comma' => true,
|
||||||
|
'namespace_no_leading_whitespace' => true,
|
||||||
|
'no_blank_lines_after_class_opening' => true,
|
||||||
|
'no_empty_lines_after_phpdocs' => true,
|
||||||
|
'object_operator' => true,
|
||||||
|
'operators_spaces' => true,
|
||||||
|
'phpdoc_align' => true,
|
||||||
|
'phpdoc_indent' => true,
|
||||||
|
'phpdoc_no_access' => true,
|
||||||
|
'phpdoc_no_package' => true,
|
||||||
|
'phpdoc_order' => true,
|
||||||
|
'phpdoc_scalar' => true,
|
||||||
|
'phpdoc_trim' => true,
|
||||||
|
'phpdoc_type_to_var' => true,
|
||||||
|
'psr0' => true,
|
||||||
|
'return' => true,
|
||||||
|
'remove_leading_slash_use' => true,
|
||||||
|
'remove_lines_between_uses' => true,
|
||||||
|
'single_array_no_trailing_comma' => true,
|
||||||
|
'single_blank_line_before_namespace' => true,
|
||||||
|
'spaces_cast' => true,
|
||||||
|
'standardize_not_equal' => true,
|
||||||
|
'ternary_spaces' => true,
|
||||||
|
'unused_use' => true,
|
||||||
|
'whitespacy_lines' => true,
|
||||||
|
))
|
||||||
|
->finder($finder)
|
||||||
|
;
|
27
.travis.yml
27
.travis.yml
|
@ -1,23 +1,42 @@
|
||||||
language: php
|
language: php
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- parallel
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.3.3
|
- 5.3.3
|
||||||
- 5.3
|
- 5.3
|
||||||
- 5.4
|
- 5.4
|
||||||
- 5.5
|
- 5.5
|
||||||
- 5.6
|
- 5.6
|
||||||
|
- 7.0
|
||||||
- hhvm
|
- hhvm
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
allow_failures:
|
||||||
|
- php: nightly
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- sudo apt-get install parallel
|
|
||||||
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
|
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
|
||||||
- composer install --prefer-source
|
- flags=""
|
||||||
- bin/composer install --prefer-source
|
- if [ `phpenv version-name` == "5.3.3" ]; then flags="--ignore-platform-reqs"; fi
|
||||||
|
- composer install $flags
|
||||||
|
- bin/composer install $flags
|
||||||
- git config --global user.name travis-ci
|
- git config --global user.name travis-ci
|
||||||
- git config --global user.email travis@example.com
|
- git config --global user.email travis@example.com
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml {};'
|
- ls -d tests/Composer/Test/* | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && $(exit 1));'
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 5
|
depth: 5
|
||||||
|
|
82
CHANGELOG.md
82
CHANGELOG.md
|
@ -1,4 +1,53 @@
|
||||||
### 1.0.0-alpha9 (2014-12-07)
|
### [1.0.0-alpha11] - 2015-10-xx
|
||||||
|
|
||||||
|
* Added config.platform to let you specify what your target environment looks like and make sure you do not inadvertently install dependencies that would break it
|
||||||
|
* Added `exclude-from-classmap` in the autoload config that lets you ignore sub-paths of classmapped directories, or psr-0/4 directories when building optimized autoloaders
|
||||||
|
* Added `path` repository type to install/symlink packages from local paths
|
||||||
|
* Added `suggests` command to show what packages are suggested, use -v to see more details
|
||||||
|
* Added `content-hash` inside the composer.lock to restrict the warnings about outdated lock file to some specific changes in the composer.json file
|
||||||
|
* Added `archive-format` and `archive-dir` config options to specify default values for the archive command
|
||||||
|
* Added --classmap-authoritative to `install`, `update`, `require`, `remove` and `dump-autoload` commands, forcing the optimized classmap to be authoritative
|
||||||
|
* Added -A / --with-dependencies to the `validate` command to allow validating all your dependencies recursively
|
||||||
|
* Added --strict to the `validate` command to treat any warning as an error that then returns a non-zero exit code
|
||||||
|
* Added a dependency on composer/semver, which is the externalized lib for all the version constraints parsing and handling
|
||||||
|
* Added support for classmap autoloading to load plugin classes and script handlers
|
||||||
|
* Added `bin-compat` config option that if set to `full` will create .bat proxy for binaries even if Compoesr runs in a linux VM
|
||||||
|
* Added SPDX 2.0 support, and externalized that in a composer/spdx-licenses lib
|
||||||
|
* Added warnings when the classmap autoloader finds duplicate classes
|
||||||
|
* Added --file to the `archive` command to choose the filename
|
||||||
|
* Added Ctrl+C handling in create-project to cancel the operation cleanly
|
||||||
|
* Fixed version guessing to use ^ always, default to stable versions, and avoid versions that require a higher php version than you have
|
||||||
|
* Fixed the lock file switching back and forth between old and new URL when a package URL is changed and many people run updates
|
||||||
|
* Fixed partial updates updating things they shouldn't when the current vendor dir was out of date with the lock file
|
||||||
|
* Fixed PHAR file creation to be more reproducible and always generate the exact same phar file from a given source
|
||||||
|
* Fixed issue when checking out git branches or tags that are also the name of a file in the repo
|
||||||
|
* Many minor fixes and documentation additions and UX improvements
|
||||||
|
|
||||||
|
### [1.0.0-alpha10] - 2015-04-14
|
||||||
|
|
||||||
|
* Break: The following event classes are deprecated and you should update your script handlers to use the new ones in type hints:
|
||||||
|
- `Composer\Script\CommandEvent` is deprecated, use `Composer\Script\Event`
|
||||||
|
- `Composer\Script\PackageEvent` is deprecated, use `Composer\Installer\PackageEvent`
|
||||||
|
* Break: Output is now split between stdout and stderr. Any irrelevant output to each command is on stderr as per unix best practices.
|
||||||
|
* Added support for npm-style semver operators (`^` and `-` ranges, ` ` = AND, `||` = OR)
|
||||||
|
* Added --prefer-lowest to `update` command to allow testing a package with the lowest declared dependencies
|
||||||
|
* Added support for parsing semver build metadata `+anything` at the end of versions
|
||||||
|
* Added --sort-packages option to `require` command for sorting dependencies
|
||||||
|
* Added --no-autoloader to `install` and `update` commands to skip autoload generation
|
||||||
|
* Added --list to `run-script` command to see available scripts
|
||||||
|
* Added --absolute to `config` command to get back absolute paths
|
||||||
|
* Added `classmap-authoritative` config option, if enabled only the classmap info will be used by the composer autoloader
|
||||||
|
* Added support for branch-alias on numeric branches
|
||||||
|
* Added support for the `https_proxy`/`HTTPS_PROXY` env vars used only for https URLs
|
||||||
|
* Added support for using real composer repos as local paths in `create-project` command
|
||||||
|
* Added --no-dev to `licenses` command
|
||||||
|
* Added support for PHP 7.0 nightly builds
|
||||||
|
* Fixed detection of stability when parsing multiple constraints
|
||||||
|
* Fixed installs from lock file containing updated composer.json requirement
|
||||||
|
* Fixed the autoloader suffix in vendor/autoload.php changing in every build
|
||||||
|
* Many minor fixes, documentation additions and UX improvements
|
||||||
|
|
||||||
|
### [1.0.0-alpha9] - 2014-12-07
|
||||||
|
|
||||||
* Added `remove` command to do the reverse of `require`
|
* Added `remove` command to do the reverse of `require`
|
||||||
* Added --ignore-platform-reqs to `install`/`update` commands to install even if you are missing a php extension or have an invalid php version
|
* Added --ignore-platform-reqs to `install`/`update` commands to install even if you are missing a php extension or have an invalid php version
|
||||||
|
@ -28,7 +77,7 @@
|
||||||
* Improved SVN and Perforce support
|
* Improved SVN and Perforce support
|
||||||
* A boatload of minor fixes, documentation additions and UX improvements
|
* A boatload of minor fixes, documentation additions and UX improvements
|
||||||
|
|
||||||
### 1.0.0-alpha8 (2014-01-06)
|
### [1.0.0-alpha8] - 2014-01-06
|
||||||
|
|
||||||
* Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements
|
* Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements
|
||||||
* Added `composer-plugin` package type to allow extensibility, and deprecated `composer-installer`
|
* Added `composer-plugin` package type to allow extensibility, and deprecated `composer-installer`
|
||||||
|
@ -59,7 +108,7 @@
|
||||||
* Improved memory usage and performance of solving dependencies
|
* Improved memory usage and performance of solving dependencies
|
||||||
* Tons of minor bug fixes and improvements
|
* Tons of minor bug fixes and improvements
|
||||||
|
|
||||||
### 1.0.0-alpha7 (2013-05-04)
|
### [1.0.0-alpha7] - 2013-05-04
|
||||||
|
|
||||||
* Break: For forward compatibility, you should change your deployment scripts to run `composer install --no-dev`. The install command will install dev dependencies by default starting in the next release
|
* Break: For forward compatibility, you should change your deployment scripts to run `composer install --no-dev`. The install command will install dev dependencies by default starting in the next release
|
||||||
* Break: The `update` command now has --dev enabled by default. --no-dev can be used to update without dev requirements, but it will create an incomplete lock file and is discouraged
|
* Break: The `update` command now has --dev enabled by default. --no-dev can be used to update without dev requirements, but it will create an incomplete lock file and is discouraged
|
||||||
|
@ -110,10 +159,10 @@
|
||||||
* Improved the coverage of the `validate` command
|
* Improved the coverage of the `validate` command
|
||||||
* Tons of minor bug fixes and improvements
|
* Tons of minor bug fixes and improvements
|
||||||
|
|
||||||
### 1.0.0-alpha6 (2012-10-23)
|
### [1.0.0-alpha6] - 2012-10-23
|
||||||
|
|
||||||
* Schema: Added ability to pass additional options to repositories (i.e. ssh keys/client certificates to secure private repos)
|
* Schema: Added ability to pass additional options to repositories (i.e. ssh keys/client certificates to secure private repos)
|
||||||
* Schema: Added a new `~` operator that should be prefered over `>=`, see http://getcomposer.org/doc/01-basic-usage.md#package-versions
|
* Schema: Added a new `~` operator that should be preferred over `>=`, see http://getcomposer.org/doc/01-basic-usage.md#package-versions
|
||||||
* Schema: Version constraints `<x.y` are assumed to be `<x.y-dev` unless specified as `<x.y-stable` to reduce confusion
|
* Schema: Version constraints `<x.y` are assumed to be `<x.y-dev` unless specified as `<x.y-stable` to reduce confusion
|
||||||
* Added `config` command to edit/list config values, including --global switch for system config
|
* Added `config` command to edit/list config values, including --global switch for system config
|
||||||
* Added OAuth token support for the GitHub API
|
* Added OAuth token support for the GitHub API
|
||||||
|
@ -121,7 +170,7 @@
|
||||||
* Added --prefer-dist flag to force installs of dev packages from zip archives instead of clones
|
* Added --prefer-dist flag to force installs of dev packages from zip archives instead of clones
|
||||||
* Added --working-dir (-d) flag to change the working directory
|
* Added --working-dir (-d) flag to change the working directory
|
||||||
* Added --profile flag to all commands to display execution time and memory usage
|
* Added --profile flag to all commands to display execution time and memory usage
|
||||||
* Added `github-protocols` config key to define the order of prefered protocols for github.com clones
|
* Added `github-protocols` config key to define the order of preferred protocols for github.com clones
|
||||||
* Added ability to interactively reset changes to vendor dirs while updating
|
* Added ability to interactively reset changes to vendor dirs while updating
|
||||||
* Added support for hg bookmarks in the hg driver
|
* Added support for hg bookmarks in the hg driver
|
||||||
* Added support for svn repositories not following the standard trunk/branch/tags scheme
|
* Added support for svn repositories not following the standard trunk/branch/tags scheme
|
||||||
|
@ -135,7 +184,7 @@
|
||||||
* Improved performance of a few essential code paths
|
* Improved performance of a few essential code paths
|
||||||
* Many bug small fixes and docs improvements
|
* Many bug small fixes and docs improvements
|
||||||
|
|
||||||
### 1.0.0-alpha5 (2012-08-18)
|
### [1.0.0-alpha5] - 2012-08-18
|
||||||
|
|
||||||
* Added `dump-autoload` command to only regenerate the autoloader
|
* Added `dump-autoload` command to only regenerate the autoloader
|
||||||
* Added --optimize to `dump-autoload` to generate a more performant classmap-based autoloader for production
|
* Added --optimize to `dump-autoload` to generate a more performant classmap-based autoloader for production
|
||||||
|
@ -153,7 +202,7 @@
|
||||||
* Improved error reporting on network failures and some other edge cases
|
* Improved error reporting on network failures and some other edge cases
|
||||||
* Various minor bug fixes and docs improvements
|
* Various minor bug fixes and docs improvements
|
||||||
|
|
||||||
### 1.0.0-alpha4 (2012-07-04)
|
### [1.0.0-alpha4] - 2012-07-04
|
||||||
|
|
||||||
* Break: The default `minimum-stability` is now `stable`, [read more](https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion)
|
* Break: The default `minimum-stability` is now `stable`, [read more](https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion)
|
||||||
* Break: Custom installers now receive the IO instance and a Composer instance in their constructor
|
* Break: Custom installers now receive the IO instance and a Composer instance in their constructor
|
||||||
|
@ -181,7 +230,7 @@
|
||||||
* Cleaned up / refactored the dependency solver code as well as the output for unsolvable requirements
|
* Cleaned up / refactored the dependency solver code as well as the output for unsolvable requirements
|
||||||
* Various bug fixes and docs improvements
|
* Various bug fixes and docs improvements
|
||||||
|
|
||||||
### 1.0.0-alpha3 (2012-05-13)
|
### [1.0.0-alpha3] - 2012-05-13
|
||||||
|
|
||||||
* Schema: Added `require-dev` for development-time requirements (tests, etc), install with --dev
|
* Schema: Added `require-dev` for development-time requirements (tests, etc), install with --dev
|
||||||
* Schema: Added author.role to list the author's role in the project
|
* Schema: Added author.role to list the author's role in the project
|
||||||
|
@ -203,7 +252,7 @@
|
||||||
* Fixed various bugs relating to package aliasing, proxy configuration, binaries
|
* Fixed various bugs relating to package aliasing, proxy configuration, binaries
|
||||||
* Various bug fixes and docs improvements
|
* Various bug fixes and docs improvements
|
||||||
|
|
||||||
### 1.0.0-alpha2 (2012-04-03)
|
### [1.0.0-alpha2] - 2012-04-03
|
||||||
|
|
||||||
* Added `create-project` command to install a project from scratch with composer
|
* Added `create-project` command to install a project from scratch with composer
|
||||||
* Added automated `classmap` autoloading support for non-PSR-0 compliant projects
|
* Added automated `classmap` autoloading support for non-PSR-0 compliant projects
|
||||||
|
@ -218,6 +267,17 @@
|
||||||
* Removed dependency on filter_var
|
* Removed dependency on filter_var
|
||||||
* Various robustness & error handling improvements, docs fixes and more bug fixes
|
* Various robustness & error handling improvements, docs fixes and more bug fixes
|
||||||
|
|
||||||
### 1.0.0-alpha1 (2012-03-01)
|
### 1.0.0-alpha1 - 2012-03-01
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
|
[1.0.0-alpha11]: https://github.com/composer/composer/compare/1.0.0-alpha10...1.0.0-alpha11
|
||||||
|
[1.0.0-alpha10]: https://github.com/composer/composer/compare/1.0.0-alpha9...1.0.0-alpha10
|
||||||
|
[1.0.0-alpha9]: https://github.com/composer/composer/compare/1.0.0-alpha8...1.0.0-alpha9
|
||||||
|
[1.0.0-alpha8]: https://github.com/composer/composer/compare/1.0.0-alpha7...1.0.0-alpha8
|
||||||
|
[1.0.0-alpha7]: https://github.com/composer/composer/compare/1.0.0-alpha6...1.0.0-alpha7
|
||||||
|
[1.0.0-alpha6]: https://github.com/composer/composer/compare/1.0.0-alpha5...1.0.0-alpha6
|
||||||
|
[1.0.0-alpha5]: https://github.com/composer/composer/compare/1.0.0-alpha4...1.0.0-alpha5
|
||||||
|
[1.0.0-alpha4]: https://github.com/composer/composer/compare/1.0.0-alpha3...1.0.0-alpha4
|
||||||
|
[1.0.0-alpha3]: https://github.com/composer/composer/compare/1.0.0-alpha2...1.0.0-alpha3
|
||||||
|
[1.0.0-alpha2]: https://github.com/composer/composer/compare/1.0.0-alpha1...1.0.0-alpha2
|
||||||
|
|
|
@ -1,29 +1,49 @@
|
||||||
Contributing to Composer
|
Contributing to Composer
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
Please note that this project is released with a
|
||||||
|
[Contributor Code of Conduct](http://contributor-covenant.org/version/1/2/0/).
|
||||||
|
By participating in this project you agree to abide by its terms.
|
||||||
|
|
||||||
|
Reporting Issues
|
||||||
|
----------------
|
||||||
|
|
||||||
|
When reporting issues, please try to be as descriptive as possible, and include
|
||||||
|
as much relevant information as you can. A step by step guide on how to
|
||||||
|
reproduce the issue will greatly increase the chances of your issue being
|
||||||
|
resolved in a timely manner.
|
||||||
|
|
||||||
|
For example, if you are experiencing a problem while running one of the
|
||||||
|
commands, please provide full output of said command in very very verbose mode
|
||||||
|
(`-vvv`, e.g. `composer install -vvv`).
|
||||||
|
|
||||||
|
If your issue involves installing, updating or resolving dependencies, the
|
||||||
|
chance of us being able to reproduce your issue will be much higher if you
|
||||||
|
share your `composer.json` with us.
|
||||||
|
|
||||||
Installation from Source
|
Installation from Source
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Prior to contributing to Composer, you must use be able to run the tests.
|
Prior to contributing to Composer, you must be able to run the test suite.
|
||||||
To achieve this, you must use the sources and not the phar file.
|
To achieve this, you need to acquire the Composer source code:
|
||||||
|
|
||||||
1. Run `git clone https://github.com/composer/composer.git`
|
1. Run `git clone https://github.com/composer/composer.git`
|
||||||
2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable
|
2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable
|
||||||
3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install`
|
3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install`
|
||||||
|
|
||||||
You can now run Composer by executing the `bin/composer` script: `php /path/to/composer/bin/composer`
|
You can run the test suite by executing `vendor/bin/phpunit` when inside the
|
||||||
|
composer directory, and run Composer by executing the `bin/composer`. To test
|
||||||
|
your modified Composer code against another project, run `php
|
||||||
|
/path/to/composer/bin/composer` inside that project's directory.
|
||||||
|
|
||||||
Contributing policy
|
Contributing policy
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
All code contributions - including those of people having commit access -
|
|
||||||
must go through a pull request and approved by a core developer before being
|
|
||||||
merged. This is to ensure proper review of all the code.
|
|
||||||
|
|
||||||
Fork the project, create a feature branch, and send us a pull request.
|
Fork the project, create a feature branch, and send us a pull request.
|
||||||
|
|
||||||
To ensure a consistent code base, you should make sure the code follows
|
To ensure a consistent code base, you should make sure the code follows
|
||||||
the [Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html)
|
the [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/). You can also
|
||||||
which we borrowed from Symfony.
|
run [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) with the
|
||||||
|
configuration file that can be found in the project root directory.
|
||||||
|
|
||||||
If you would like to help, take a look at the [list of issues](http://github.com/composer/composer/issues).
|
If you would like to help, take a look at the [list of open issues](https://github.com/composer/composer/issues).
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2011 Nils Adermann, Jordi Boggiano
|
Copyright (c) 2015 Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
33
README.md
33
README.md
|
@ -6,6 +6,8 @@ Composer helps you declare, manage and install dependencies of PHP projects, ens
|
||||||
See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation.
|
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)
|
[![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer)
|
||||||
|
[![Dependency Status](https://www.versioneye.com/php/composer:composer/dev-master/badge.svg)](https://www.versioneye.com/php/composer:composer/dev-master)
|
||||||
|
[![Reference Status](https://www.versioneye.com/php/composer:composer/reference_badge.svg?style=flat)](https://www.versioneye.com/php/composer:composer/references)
|
||||||
|
|
||||||
Installation / Usage
|
Installation / Usage
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -16,10 +18,17 @@ Installation / Usage
|
||||||
$ curl -sS https://getcomposer.org/installer | php
|
$ curl -sS https://getcomposer.org/installer | php
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note:** If the above fails for some reason, you can download the installer
|
||||||
|
> with `php` instead:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
|
```
|
||||||
|
|
||||||
2. Create a composer.json defining your dependencies. Note that this example is
|
2. Create a composer.json defining your dependencies. Note that this example is
|
||||||
a short version for applications that are not meant to be published as packages
|
a short version for applications that are not meant to be published as packages
|
||||||
themselves. To create libraries/packages please read the
|
themselves. To create libraries/packages please read the
|
||||||
[documentation](http://getcomposer.org/doc/02-libraries.md).
|
[documentation](https://getcomposer.org/doc/02-libraries.md).
|
||||||
|
|
||||||
``` json
|
``` json
|
||||||
{
|
{
|
||||||
|
@ -35,26 +44,26 @@ themselves. To create libraries/packages please read the
|
||||||
Global installation of Composer (manual)
|
Global installation of Composer (manual)
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
Follow instructions [in the documentation](http://getcomposer.org/doc/00-intro.md#globally)
|
Follow instructions [in the documentation](https://getcomposer.org/doc/00-intro.md#globally)
|
||||||
|
|
||||||
Updating Composer
|
Updating Composer
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Running `php composer.phar self-update` or equivalent will update a phar
|
Running `php composer.phar self-update` or equivalent will update a phar
|
||||||
install with the latest version.
|
install to the latest version.
|
||||||
|
|
||||||
|
|
||||||
Community
|
Community
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Mailing lists for [user support](http://groups.google.com/group/composer-users) and
|
|
||||||
[development](http://groups.google.com/group/composer-dev).
|
|
||||||
|
|
||||||
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
|
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
|
||||||
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
|
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
|
||||||
|
|
||||||
Stack Overflow has a growing collection of
|
For support, Stack Overflow also offers a good collection of
|
||||||
[Composer related questions](http://stackoverflow.com/questions/tagged/composer-php).
|
[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
|
||||||
|
|
||||||
|
Please note that this project is released with a
|
||||||
|
[Contributor Code of Conduct](http://contributor-covenant.org/version/1/2/0/).
|
||||||
|
By participating in this project and its community you agree to abide by those terms.
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
@ -64,8 +73,8 @@ PHP 5.3.2 or above (at least 5.3.4 recommended to avoid potential bugs)
|
||||||
Authors
|
Authors
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Nils Adermann - <naderman@naderman.de> - <http://twitter.com/naderman> - <http://www.naderman.de><br />
|
Nils Adermann - <naderman@naderman.de> - <https://twitter.com/naderman> - <http://www.naderman.de><br />
|
||||||
Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek> - <http://seld.be><br />
|
Jordi Boggiano - <j.boggiano@seld.be> - <https://twitter.com/seldaek> - <http://seld.be><br />
|
||||||
|
|
||||||
See also the list of [contributors](https://github.com/composer/composer/contributors) who participated in this project.
|
See also the list of [contributors](https://github.com/composer/composer/contributors) who participated in this project.
|
||||||
|
|
||||||
|
@ -78,6 +87,6 @@ Acknowledgments
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
- This project's Solver started out as a PHP port of openSUSE's
|
- This project's Solver started out as a PHP port of openSUSE's
|
||||||
[Libzypp satsolver](http://en.opensuse.org/openSUSE:Libzypp_satsolver).
|
[Libzypp satsolver](https://en.opensuse.org/openSUSE:Libzypp_satsolver).
|
||||||
- This project uses hiddeninput.exe to prompt for passwords on windows, sources
|
- This project uses hiddeninput.exe to prompt for passwords on windows, sources
|
||||||
and details can be found on the [github page of the project](https://github.com/Seldaek/hidden-input).
|
and details can be found on the [github page of the project](https://github.com/Seldaek/hidden-input).
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
build: false
|
||||||
|
shallow_clone: true
|
||||||
|
platform:
|
||||||
|
- x86
|
||||||
|
- x64
|
||||||
|
clone_folder: c:\projects\composer
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- c:\tools\php -> appveyor.yml
|
||||||
|
|
||||||
|
init:
|
||||||
|
- SET PATH=C:\Program Files\OpenSSL;c:\tools\php;%PATH%
|
||||||
|
- SET COMPOSER_NO_INTERACTION=1
|
||||||
|
- SET PHP=1
|
||||||
|
- SET ANSICON=121x90 (121x90)
|
||||||
|
|
||||||
|
install:
|
||||||
|
- IF EXIST c:\tools\php (SET PHP=0)
|
||||||
|
- IF %PHP%==1 cinst -y OpenSSL.Light
|
||||||
|
- IF %PHP%==1 cinst -y php
|
||||||
|
- cd c:\tools\php
|
||||||
|
- IF %PHP%==1 copy php.ini-production php.ini /Y
|
||||||
|
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini
|
||||||
|
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||||
|
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
||||||
|
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
|
||||||
|
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
|
||||||
|
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
|
||||||
|
- appveyor DownloadFile https://getcomposer.org/composer.phar
|
||||||
|
- cd c:\projects\composer
|
||||||
|
- composer install --prefer-source --no-progress
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- cd c:\projects\composer
|
||||||
|
- vendor/bin/phpunit --colors=always
|
16
bin/compile
16
bin/compile
|
@ -1,6 +1,20 @@
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
$cwd = getcwd();
|
||||||
|
chdir(__DIR__.'/../');
|
||||||
|
$ts = rtrim(shell_exec('git log -n1 --pretty=%ct HEAD'));
|
||||||
|
if (!is_numeric($ts)) {
|
||||||
|
echo 'Could not detect date using "git log -n1 --pretty=%ct HEAD"'.PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Install with the current version to force it having the right ClassLoader version
|
||||||
|
// Install without dev packages to clean up the included classmap from phpunit classes
|
||||||
|
shell_exec('php bin/composer config autoloader-suffix ComposerPhar' . $ts);
|
||||||
|
shell_exec('php bin/composer install -q --no-dev');
|
||||||
|
shell_exec('php bin/composer config autoloader-suffix --unset');
|
||||||
|
chdir($cwd);
|
||||||
|
|
||||||
require __DIR__.'/../src/bootstrap.php';
|
require __DIR__.'/../src/bootstrap.php';
|
||||||
|
|
||||||
use Composer\Compiler;
|
use Composer\Compiler;
|
||||||
|
@ -12,6 +26,6 @@ try {
|
||||||
$compiler = new Compiler();
|
$compiler = new Compiler();
|
||||||
$compiler->compile();
|
$compiler->compile();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo 'Failed to compile phar: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine();
|
echo 'Failed to compile phar: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().PHP_EOL;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,9 @@ if (function_exists('ini_set')) {
|
||||||
};
|
};
|
||||||
|
|
||||||
$memoryLimit = trim(ini_get('memory_limit'));
|
$memoryLimit = trim(ini_get('memory_limit'));
|
||||||
// Increase memory_limit if it is lower than 512M
|
// Increase memory_limit if it is lower than 1GB
|
||||||
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 512 * 1024 * 1024) {
|
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1024) {
|
||||||
@ini_set('memory_limit', '512M');
|
@ini_set('memory_limit', '1G');
|
||||||
}
|
}
|
||||||
unset($memoryInBytes, $memoryLimit);
|
unset($memoryInBytes, $memoryLimit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$identifiers = new SPDXLicenseIdentifiersOnline;
|
|
||||||
$printer = new JsonPrinter;
|
|
||||||
$printer->printStringArray($identifiers->getStrings());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SPDX Identifier List from the registry.
|
|
||||||
*/
|
|
||||||
class SPDXLicenseIdentifiersOnline
|
|
||||||
{
|
|
||||||
const REGISTRY = 'http://www.spdx.org/licenses/';
|
|
||||||
const EXPRESSION = '//*[@typeof="spdx:License"]/code[@property="spdx:licenseId"]/text()';
|
|
||||||
|
|
||||||
private $identifiers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getStrings()
|
|
||||||
{
|
|
||||||
if ($this->identifiers) {
|
|
||||||
return $this->identifiers;
|
|
||||||
}
|
|
||||||
$this->identifiers = $this->importNodesFromURL(
|
|
||||||
self::REGISTRY,
|
|
||||||
self::EXPRESSION
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->identifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function importNodesFromURL($url, $expressionTextNodes)
|
|
||||||
{
|
|
||||||
$doc = new DOMDocument();
|
|
||||||
$doc->loadHTMLFile($url);
|
|
||||||
$xp = new DOMXPath($doc);
|
|
||||||
$codes = $xp->query($expressionTextNodes);
|
|
||||||
if (!$codes) {
|
|
||||||
throw new \Exception(sprintf('XPath query failed: %s', $expressionTextNodes));
|
|
||||||
}
|
|
||||||
if ($codes->length < 20) {
|
|
||||||
throw new \Exception('Obtaining the license table failed, there can not be less than 20 identifiers.');
|
|
||||||
}
|
|
||||||
$identifiers = array();
|
|
||||||
foreach ($codes as $code) {
|
|
||||||
$identifiers[] = $code->nodeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $identifiers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print an array the way this script needs it.
|
|
||||||
*/
|
|
||||||
class JsonPrinter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param array $array
|
|
||||||
*/
|
|
||||||
public function printStringArray(array $array)
|
|
||||||
{
|
|
||||||
$lines = array('');
|
|
||||||
$line = &$lines[0];
|
|
||||||
$last = count($array) - 1;
|
|
||||||
foreach ($array as $item => $code) {
|
|
||||||
$code = sprintf('"%s"%s', trim($code), $item === $last ? '' : ', ');
|
|
||||||
$length = strlen($line) + strlen($code) - 1;
|
|
||||||
if ($length > 76) {
|
|
||||||
$line = rtrim($line);
|
|
||||||
unset($line);
|
|
||||||
$lines[] = $code;
|
|
||||||
$line = &$lines[count($lines) - 1];
|
|
||||||
} else {
|
|
||||||
$line .= $code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$json = sprintf("[%s]", implode("\n ", $lines));
|
|
||||||
$json = str_replace(array("[\"", "\"]"), array("[\n \"", "\"\n]"), $json);
|
|
||||||
echo $json;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "composer/composer",
|
"name": "composer/composer",
|
||||||
"description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
|
"description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
|
||||||
"keywords": ["package", "dependency", "autoload"],
|
"keywords": ["package", "dependency", "autoload"],
|
||||||
"homepage": "http://getcomposer.org/",
|
"homepage": "https://getcomposer.org/",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -22,25 +22,37 @@
|
||||||
"issues": "https://github.com/composer/composer/issues"
|
"issues": "https://github.com/composer/composer/issues"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.2",
|
"php": "^5.3.2 || ^7.0",
|
||||||
"justinrainbow/json-schema": "~1.3",
|
"justinrainbow/json-schema": "^1.4.4",
|
||||||
"seld/jsonlint": "~1.0",
|
"composer/spdx-licenses": "^1.0",
|
||||||
"symfony/console": "~2.5",
|
"composer/semver": "^1.0",
|
||||||
"symfony/finder": "~2.2",
|
"seld/jsonlint": "^1.0",
|
||||||
"symfony/process": "~2.1"
|
"symfony/console": "^2.5 || ^3.0",
|
||||||
|
"symfony/finder": "^2.2 || ^3.0",
|
||||||
|
"symfony/process": "^2.1 || ^3.0",
|
||||||
|
"symfony/filesystem": "^2.5 || ^3.0",
|
||||||
|
"seld/phar-utils": "^1.0",
|
||||||
|
"seld/cli-prompt": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "~4.5"
|
"phpunit/phpunit": "^4.5 || ^5.0.5",
|
||||||
|
"phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis",
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "5.3.9"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic",
|
"ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic",
|
||||||
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
|
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Composer": "src/" }
|
"psr-4": { "Composer\\": "src/Composer" }
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-0": { "Composer\\Test": "tests/" }
|
"psr-4": { "Composer\\Test\\": "tests/Composer/Test" }
|
||||||
},
|
},
|
||||||
"bin": ["bin/composer"],
|
"bin": ["bin/composer"],
|
||||||
"extra": {
|
"extra": {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
155
doc/00-intro.md
155
doc/00-intro.md
|
@ -1,54 +1,41 @@
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Composer is a tool for dependency management in PHP. It allows you to declare
|
Composer is a tool for dependency management in PHP. It allows you to declare
|
||||||
the dependent libraries your project needs and it will install them in your
|
the libraries your project depends on and it will manage (install/update) them
|
||||||
project for you.
|
for you.
|
||||||
|
|
||||||
## Dependency management
|
## Dependency management
|
||||||
|
|
||||||
Composer is not a package manager. Yes, it deals with "packages" or libraries, but
|
Composer is **not** a package manager in the same sense as Yum or Apt are. Yes,
|
||||||
it manages them on a per-project basis, installing them in a directory (e.g. `vendor`)
|
it deals with "packages" or libraries, but it manages them on a per-project
|
||||||
inside your project. By default it will never install anything globally. Thus,
|
basis, installing them in a directory (e.g. `vendor`) inside your project. By
|
||||||
it is a dependency manager.
|
default it will never install anything globally. Thus, it is a dependency
|
||||||
|
manager.
|
||||||
|
|
||||||
This idea is not new and Composer is strongly inspired by node's [npm](http://npmjs.org/)
|
This idea is not new and Composer is strongly inspired by node's
|
||||||
and ruby's [bundler](http://gembundler.com/). But there has not been such a tool
|
[npm](https://npmjs.org/) and ruby's [bundler](http://bundler.io/).
|
||||||
for PHP.
|
|
||||||
|
|
||||||
The problem that Composer solves is this:
|
Suppose:
|
||||||
|
|
||||||
a) You have a project that depends on a number of libraries.
|
a) You have a project that depends on a number of libraries.
|
||||||
|
|
||||||
b) Some of those libraries depend on other libraries.
|
b) Some of those libraries depend on other libraries.
|
||||||
|
|
||||||
c) You declare the things you depend on.
|
Composer:
|
||||||
|
|
||||||
d) Composer finds out which versions of which packages need to be installed, and
|
c) Enables you to declare the libraries you depend on.
|
||||||
|
|
||||||
|
d) Finds out which versions of which packages can and need to be installed, and
|
||||||
installs them (meaning it downloads them into your project).
|
installs them (meaning it downloads them into your project).
|
||||||
|
|
||||||
## Declaring dependencies
|
See the [Basic usage](01-basic-usage.md) chapter for more details on declaring
|
||||||
|
dependencies.
|
||||||
Let's say you are creating a project, and you need a library that does logging.
|
|
||||||
You decide to use [monolog](https://github.com/Seldaek/monolog). In order to
|
|
||||||
add it to your project, all you need to do is create a `composer.json` file
|
|
||||||
which describes the project's dependencies.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"monolog/monolog": "1.2.*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We are simply stating that our project requires some `monolog/monolog` package,
|
|
||||||
any version beginning with `1.2`.
|
|
||||||
|
|
||||||
## System Requirements
|
## System Requirements
|
||||||
|
|
||||||
Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile
|
Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile
|
||||||
flags are also required, but when using the installer you will be warned about any
|
flags are also required, but when using the installer you will be warned about
|
||||||
incompatibilities.
|
any incompatibilities.
|
||||||
|
|
||||||
To install packages from sources instead of simple zip archives, you will need
|
To install packages from sources instead of simple zip archives, you will need
|
||||||
git, svn or hg depending on how the package is version-controlled.
|
git, svn or hg depending on how the package is version-controlled.
|
||||||
|
@ -60,6 +47,12 @@ Linux and OSX.
|
||||||
|
|
||||||
### Downloading the Composer Executable
|
### Downloading the Composer Executable
|
||||||
|
|
||||||
|
Composer offers a convenient installer that you can execute directly from the
|
||||||
|
commandline. Feel free to [download this file](https://getcomposer.org/installer)
|
||||||
|
or review it on [GitHub](https://github.com/composer/getcomposer.org/blob/master/web/installer)
|
||||||
|
if you wish to know more about the inner workings of the installer. The source
|
||||||
|
is plain PHP.
|
||||||
|
|
||||||
There are in short, two ways to install Composer. Locally as part of your
|
There are in short, two ways to install Composer. Locally as part of your
|
||||||
project, or globally as a system wide executable.
|
project, or globally as a system wide executable.
|
||||||
|
|
||||||
|
@ -79,37 +72,54 @@ curl -sS https://getcomposer.org/installer | php
|
||||||
php -r "readfile('https://getcomposer.org/installer');" | php
|
php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer will just check a few PHP settings and then download `composer.phar`
|
The installer will just check a few PHP settings and then download
|
||||||
to your working directory. This file is the Composer binary. It is a PHAR (PHP
|
`composer.phar` to your working directory. This file is the Composer binary. It
|
||||||
archive), which is an archive format for PHP which can be run on the command
|
is a PHAR (PHP archive), which is an archive format for PHP which can be run on
|
||||||
line, amongst other things.
|
the command line, amongst other things.
|
||||||
|
|
||||||
|
Now just run `php composer.phar` in order to run Composer.
|
||||||
|
|
||||||
You can install Composer to a specific directory by using the `--install-dir`
|
You can install Composer to a specific directory by using the `--install-dir`
|
||||||
option and providing a target directory (it can be an absolute or relative path):
|
option and additionally (re)name it as well using the `--filename` option:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -sS https://getcomposer.org/installer | php -- --install-dir=bin
|
curl -sS https://getcomposer.org/installer | php -- --install-dir=bin --filename=composer
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Now just run `php bin/composer` in order to run Composer.
|
||||||
|
|
||||||
#### Globally
|
#### Globally
|
||||||
|
|
||||||
You can place this file anywhere you wish. If you put it in your `PATH`,
|
You can place the Composer PHAR anywhere you wish. If you put it in a directory
|
||||||
you can access it globally. On unixy systems you can even make it
|
that is part of your `PATH`, you can access it globally. On unixy systems you
|
||||||
executable and invoke it without `php`.
|
can even make it executable and invoke it without directly using the `php`
|
||||||
|
interpreter.
|
||||||
|
|
||||||
You can run these commands to easily access `composer` from anywhere on your system:
|
Run these commands to globally install `composer` on your system:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -sS https://getcomposer.org/installer | php
|
curl -sS https://getcomposer.org/installer | php
|
||||||
mv composer.phar /usr/local/bin/composer
|
mv composer.phar /usr/local/bin/composer
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** If the above fails due to permissions, run the `mv` line
|
> **Note:** If the above fails due to permissions, run the `mv` line again
|
||||||
> again with sudo.
|
> with sudo.
|
||||||
|
|
||||||
> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you receive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/` manually before proceeding.
|
A quick copy-paste version including sudo:
|
||||||
|
|
||||||
Then, just run `composer` in order to run Composer instead of `php composer.phar`.
|
```sh
|
||||||
|
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** On some versions of OSX the `/usr` directory does not exist by
|
||||||
|
> default. If you receive the error "/usr/local/bin/composer: No such file or
|
||||||
|
> directory" then you must create the directory manually before proceeding:
|
||||||
|
> `mkdir -p /usr/local/bin`.
|
||||||
|
|
||||||
|
> **Note:** For information on changing your PATH, please read the
|
||||||
|
> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use Google.
|
||||||
|
|
||||||
|
Now just run `composer` in order to run Composer instead of `php composer.phar`.
|
||||||
|
|
||||||
## Installation - Windows
|
## Installation - Windows
|
||||||
|
|
||||||
|
@ -117,24 +127,26 @@ Then, just run `composer` in order to run Composer instead of `php composer.phar
|
||||||
|
|
||||||
This is the easiest way to get Composer set up on your machine.
|
This is the easiest way to get Composer set up on your machine.
|
||||||
|
|
||||||
Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe),
|
Download and run
|
||||||
it will install the latest Composer version and set up your PATH so that you can
|
[Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). It will
|
||||||
just call `composer` from any directory in your command line.
|
install the latest Composer version and set up your PATH so that you can just
|
||||||
|
call `composer` from any directory in your command line.
|
||||||
|
|
||||||
> **Note:** Close your current terminal. Test usage with a new terminal:
|
> **Note:** Close your current terminal. Test usage with a new terminal: This is
|
||||||
> That is important since the PATH only gets loaded when the terminal starts.
|
> important since the PATH only gets loaded when the terminal starts.
|
||||||
|
|
||||||
### Manual Installation
|
### Manual Installation
|
||||||
|
|
||||||
Change to a directory on your `PATH` and run the install snippet to download
|
Change to a directory on your `PATH` and run the install snippet to download
|
||||||
composer.phar:
|
`composer.phar`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
C:\Users\username>cd C:\bin
|
C:\Users\username>cd C:\bin
|
||||||
C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php
|
C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** If the above fails due to readfile, use the `http` url or enable php_openssl.dll in php.ini
|
> **Note:** If the above fails due to readfile, use the `http` url or enable
|
||||||
|
> php_openssl.dll in php.ini
|
||||||
|
|
||||||
Create a new `composer.bat` file alongside `composer.phar`:
|
Create a new `composer.bat` file alongside `composer.phar`:
|
||||||
|
|
||||||
|
@ -142,6 +154,8 @@ Create a new `composer.bat` file alongside `composer.phar`:
|
||||||
C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat
|
C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Add the directory to your PATH environment variable if it isn't already.
|
||||||
|
|
||||||
Close your current terminal. Test usage with a new terminal:
|
Close your current terminal. Test usage with a new terminal:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -151,38 +165,7 @@ Composer version 27d8904
|
||||||
|
|
||||||
## Using Composer
|
## Using Composer
|
||||||
|
|
||||||
We will now use Composer to install the dependencies of the project. If you
|
Now that you've installed Composer, you are ready to use it! Head on over to the
|
||||||
don't have a `composer.json` file in the current directory please skip to the
|
next chapter for a short and simple demonstration.
|
||||||
[Basic Usage](01-basic-usage.md) chapter.
|
|
||||||
|
|
||||||
To resolve and download dependencies, run the `install` command:
|
[Basic usage](01-basic-usage.md) →
|
||||||
|
|
||||||
```sh
|
|
||||||
php composer.phar install
|
|
||||||
```
|
|
||||||
|
|
||||||
If you did a global install and do not have the phar in that directory
|
|
||||||
run this instead:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
composer install
|
|
||||||
```
|
|
||||||
|
|
||||||
Following the [example above](#declaring-dependencies), this will download
|
|
||||||
monolog into the `vendor/monolog/monolog` directory.
|
|
||||||
|
|
||||||
## Autoloading
|
|
||||||
|
|
||||||
Besides downloading the library, Composer also prepares an autoload file that's
|
|
||||||
capable of autoloading all of the classes in any of the libraries that it
|
|
||||||
downloads. To use it, just add the following line to your code's bootstrap
|
|
||||||
process:
|
|
||||||
|
|
||||||
```php
|
|
||||||
require 'vendor/autoload.php';
|
|
||||||
```
|
|
||||||
|
|
||||||
Woah! Now start using monolog! To keep learning more about Composer, keep
|
|
||||||
reading the "Basic Usage" chapter.
|
|
||||||
|
|
||||||
[Basic Usage](01-basic-usage.md) →
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
# Basic usage
|
# Basic usage
|
||||||
|
|
||||||
## Installing
|
## Introduction
|
||||||
|
|
||||||
If you have not yet installed Composer, refer to the [Intro](00-intro.md) chapter.
|
For our basic usage introduction, we will be installing `monolog/monolog`,
|
||||||
|
a logging library. If you have not yet installed Composer, refer to the
|
||||||
|
[Intro](00-intro.md) chapter.
|
||||||
|
|
||||||
|
> **Note:** for the sake of simplicity, this introduction will assume you
|
||||||
|
> have performed a [local](00-intro.md#locally) install of Composer.
|
||||||
|
|
||||||
## `composer.json`: Project Setup
|
## `composer.json`: Project Setup
|
||||||
|
|
||||||
|
@ -10,14 +15,11 @@ To start using Composer in your project, all you need is a `composer.json`
|
||||||
file. This file describes the dependencies of your project and may contain
|
file. This file describes the dependencies of your project and may contain
|
||||||
other metadata as well.
|
other metadata as well.
|
||||||
|
|
||||||
The [JSON format](http://json.org/) is quite easy to write. It allows you to
|
|
||||||
define nested structures.
|
|
||||||
|
|
||||||
### The `require` Key
|
### The `require` Key
|
||||||
|
|
||||||
The first (and often only) thing you specify in `composer.json` is the
|
The first (and often only) thing you specify in `composer.json` is the
|
||||||
`require` key. You're simply telling Composer which packages your project
|
[`require`](04-schema.md#require) key. You're simply telling Composer which
|
||||||
depends on.
|
packages your project depends on.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -27,15 +29,16 @@ depends on.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, `require` takes an object that maps **package names** (e.g. `monolog/monolog`)
|
As you can see, [`require`](04-schema.md#require) takes an object that maps
|
||||||
to **package versions** (e.g. `1.0.*`).
|
**package names** (e.g. `monolog/monolog`) to **version constraints** (e.g.
|
||||||
|
`1.0.*`).
|
||||||
|
|
||||||
### Package Names
|
### Package Names
|
||||||
|
|
||||||
The package name consists of a vendor name and the project's name. Often these
|
The package name consists of a vendor name and the project's name. Often these
|
||||||
will be identical - the vendor name just exists to prevent naming clashes. It allows
|
will be identical - the vendor name just exists to prevent naming clashes. It
|
||||||
two different people to create a library named `json`, which would then just be
|
allows two different people to create a library named `json`, which would then
|
||||||
named `igorw/json` and `seldaek/json`.
|
just be named `igorw/json` and `seldaek/json`.
|
||||||
|
|
||||||
Here we are requiring `monolog/monolog`, so the vendor name is the same as the
|
Here we are requiring `monolog/monolog`, so the vendor name is the same as the
|
||||||
project's name. For projects with a unique name this is recommended. It also
|
project's name. For projects with a unique name this is recommended. It also
|
||||||
|
@ -45,59 +48,26 @@ smaller decoupled parts.
|
||||||
|
|
||||||
### Package Versions
|
### Package Versions
|
||||||
|
|
||||||
In the previous example we were requiring version `1.0.*` of monolog. This
|
In the previous example we were requiring version
|
||||||
means any version in the `1.0` development branch. It would match `1.0.0`,
|
[`1.0.*`](http://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*) of
|
||||||
`1.0.2` or `1.0.20`.
|
Monolog. This means any version in the `1.0` development branch. It is the
|
||||||
|
equivalent of saying versions that match `>=1.0 <1.1`.
|
||||||
|
|
||||||
Version constraints can be specified in a few different ways.
|
Version constraints can be specified in several ways, read
|
||||||
|
[versions](articles/versions.md) for more in-depth information on this topic.
|
||||||
Name | Example | Description
|
|
||||||
-------------- | ------------------------------------------------------------------------ | -----------
|
|
||||||
Exact version | `1.0.2` | You can specify the exact version of a package.
|
|
||||||
Range | `>=1.0` `>=1.0 <2.0` <code>>=1.0 <1.1 || >=1.2</code> | By using comparison operators you can specify ranges of valid versions. Valid operators are `>`, `>=`, `<`, `<=`, `!=`. <br />You can define multiple ranges. Ranges separated by a space (<code> </code>) or comma (`,`) will be treated as a **logical AND**. A double pipe (<code>||</code>) will be treated as a **logical OR**. AND has higher precedence than OR.
|
|
||||||
Hyphen Range | `1.0 - 2.0` | Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the `2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to `>=1.0.0 <=2.1.0`.
|
|
||||||
Wildcard | `1.0.*` | You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`.
|
|
||||||
Tilde Operator | `~1.2` | Very useful for projects that follow semantic versioning. `~1.2` is equivalent to `>=1.2 <2.0`. For more details, read the next section below.
|
|
||||||
Caret Operator | `^1.2.3` | Very useful for projects that follow semantic versioning. `^1.2.3` is equivalent to `>=1.2.3 <2.0`. For more details, read the next section below.
|
|
||||||
|
|
||||||
### Next Significant Release (Tilde and Caret Operators)
|
|
||||||
|
|
||||||
The `~` operator is best explained by example: `~1.2` is equivalent to
|
|
||||||
`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
|
|
||||||
it is mostly useful for projects respecting [semantic
|
|
||||||
versioning](http://semver.org/). A common usage would be to mark the minimum
|
|
||||||
minor version you depend on, like `~1.2` (which allows anything up to, but not
|
|
||||||
including, 2.0). Since in theory there should be no backwards compatibility
|
|
||||||
breaks until 2.0, that works well. Another way of looking at it is that using
|
|
||||||
`~` specifies a minimum version, but allows the last digit specified to go up.
|
|
||||||
|
|
||||||
The `^` operator behaves very similarly but it sticks closer to semantic
|
|
||||||
versioning, and will always allow non-breaking updates. For example `^1.2.3`
|
|
||||||
is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should
|
|
||||||
break backwards compatibility. For pre-1.0 versions it also acts with safety
|
|
||||||
in mind and treats `^0.3` as `>=0.3.0 <0.4.0`
|
|
||||||
|
|
||||||
> **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint
|
|
||||||
> like `~1.2` would not install it. As said above `~1.2` only means the `.2`
|
|
||||||
> can change but the `1.` part is fixed.
|
|
||||||
|
|
||||||
> **Note:** The `~` operator has an exception on its behavior for the major
|
|
||||||
> release number. This means for example that `~1` is the same as `~1.0` as
|
|
||||||
> it will not allow the major number to increase trying to keep backwards
|
|
||||||
> compatibility.
|
|
||||||
|
|
||||||
### Stability
|
### Stability
|
||||||
|
|
||||||
By default only stable releases are taken into consideration. If you would like
|
By default only stable releases are taken into consideration. If you would
|
||||||
to also get RC, beta, alpha or dev versions of your dependencies you can do
|
like to also get RC, beta, alpha or dev versions of your dependencies you can
|
||||||
so using [stability flags](04-schema.md#package-links). To change that for all
|
do so using [stability flags](04-schema.md#package-links). To change that for
|
||||||
packages instead of doing per dependency you can also use the
|
all packages instead of doing per dependency you can also use the
|
||||||
[minimum-stability](04-schema.md#minimum-stability) setting.
|
[minimum-stability](04-schema.md#minimum-stability) setting.
|
||||||
|
|
||||||
## Installing Dependencies
|
## Installing Dependencies
|
||||||
|
|
||||||
To fetch the defined dependencies into your local project, just run the
|
To install the defined dependencies for your project, just run the
|
||||||
`install` command of `composer.phar`.
|
[`install`](03-cli.md#install) command.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
php composer.phar install
|
php composer.phar install
|
||||||
|
@ -106,14 +76,14 @@ php composer.phar install
|
||||||
This will find the latest version of `monolog/monolog` that matches the
|
This will find the latest version of `monolog/monolog` that matches the
|
||||||
supplied version constraint and download it into the `vendor` directory.
|
supplied version constraint and download it into the `vendor` directory.
|
||||||
It's a convention to put third party code into a directory named `vendor`.
|
It's a convention to put third party code into a directory named `vendor`.
|
||||||
In case of monolog it will put it into `vendor/monolog/monolog`.
|
In case of Monolog it will put it into `vendor/monolog/monolog`.
|
||||||
|
|
||||||
> **Tip:** If you are using git for your project, you probably want to add
|
> **Tip:** If you are using git for your project, you probably want to add
|
||||||
> `vendor` into your `.gitignore`. You really don't want to add all of that
|
> `vendor` in your `.gitignore`. You really don't want to add all of that
|
||||||
> code to your repository.
|
> code to your repository.
|
||||||
|
|
||||||
Another thing that the `install` command does is it adds a `composer.lock`
|
You will notice the [`install`](03-cli.md#install) command also created a
|
||||||
file into your project root.
|
`composer.lock` file.
|
||||||
|
|
||||||
## `composer.lock` - The Lock File
|
## `composer.lock` - The Lock File
|
||||||
|
|
||||||
|
@ -121,34 +91,36 @@ After installing the dependencies, Composer writes the list of the exact
|
||||||
versions it installed into a `composer.lock` file. This locks the project
|
versions it installed into a `composer.lock` file. This locks the project
|
||||||
to those specific versions.
|
to those specific versions.
|
||||||
|
|
||||||
**Commit your application's `composer.lock` (along with `composer.json`) into version control.**
|
**Commit your application's `composer.lock` (along with `composer.json`)
|
||||||
|
into version control.**
|
||||||
|
|
||||||
This is important because the `install` command checks if a lock file is present,
|
This is important because the [`install`](03-cli.md#install) command checks
|
||||||
and if it is, it downloads the versions specified there (regardless of what `composer.json`
|
if a lock file is present, and if it is, it downloads the versions specified
|
||||||
says).
|
there (regardless of what `composer.json` says).
|
||||||
|
|
||||||
This means that anyone who sets up the project will download the exact
|
This means that anyone who sets up the project will download the exact same
|
||||||
same version of the dependencies. Your CI server, production machines, other
|
version of the dependencies. Your CI server, production machines, other
|
||||||
developers in your team, everything and everyone runs on the same dependencies, which
|
developers in your team, everything and everyone runs on the same dependencies,
|
||||||
mitigates the potential for bugs affecting only some parts of the deployments. Even if you
|
which mitigates the potential for bugs affecting only some parts of the
|
||||||
develop alone, in six months when reinstalling the project you can feel confident the
|
deployments. Even if you develop alone, in six months when reinstalling the
|
||||||
dependencies installed are still working even if your dependencies released
|
project you can feel confident the dependencies installed are still working even
|
||||||
many new versions since then.
|
if your dependencies released many new versions since then.
|
||||||
|
|
||||||
If no `composer.lock` file exists, Composer will read the dependencies and
|
If no `composer.lock` file exists, Composer will read the dependencies and
|
||||||
versions from `composer.json` and create the lock file after executing the `update` or the `install`
|
versions from `composer.json` and create the lock file after executing the
|
||||||
command.
|
[`update`](03-cli.md#update) or the [`install`](03-cli.md#install) command.
|
||||||
|
|
||||||
This means that if any of the dependencies get a new version, you won't get the updates
|
This means that if any of the dependencies get a new version, you won't get the
|
||||||
automatically. To update to the new version, use `update` command. This will fetch
|
updates automatically. To update to the new version, use the
|
||||||
the latest matching versions (according to your `composer.json` file) and also update
|
[`update`](03-cli.md#update) command. This will fetch the latest matching
|
||||||
the lock file with the new version.
|
versions (according to your `composer.json` file) and also update the lock file
|
||||||
|
with the new version.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
php composer.phar update
|
php composer.phar update
|
||||||
```
|
```
|
||||||
> **Note:** Composer will display a Warning when executing an `install` command if
|
> **Note:** Composer will display a Warning when executing an `install` command
|
||||||
`composer.lock` and `composer.json` are not synchronized.
|
> if `composer.lock` and `composer.json` are not synchronized.
|
||||||
|
|
||||||
If you only want to install or update one dependency, you can whitelist them:
|
If you only want to install or update one dependency, you can whitelist them:
|
||||||
|
|
||||||
|
@ -156,47 +128,45 @@ If you only want to install or update one dependency, you can whitelist them:
|
||||||
php composer.phar update monolog/monolog [...]
|
php composer.phar update monolog/monolog [...]
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** For libraries it is not necessarily recommended to commit the lock file,
|
> **Note:** For libraries it is not necessary to commit the lock
|
||||||
> see also: [Libraries - Lock file](02-libraries.md#lock-file).
|
> file, see also: [Libraries - Lock file](02-libraries.md#lock-file).
|
||||||
|
|
||||||
## Packagist
|
## Packagist
|
||||||
|
|
||||||
[Packagist](https://packagist.org/) is the main Composer repository. A Composer
|
[Packagist](https://packagist.org/) is the main Composer repository. A Composer
|
||||||
repository is basically a package source: a place where you can get packages
|
repository is basically a package source: a place where you can get packages
|
||||||
from. Packagist aims to be the central repository that everybody uses. This
|
from. Packagist aims to be the central repository that everybody uses. This
|
||||||
means that you can automatically `require` any package that is available
|
means that you can automatically `require` any package that is available there.
|
||||||
there.
|
|
||||||
|
|
||||||
If you go to the [packagist website](https://packagist.org/) (packagist.org),
|
If you go to the [Packagist website](https://packagist.org/) (packagist.org),
|
||||||
you can browse and search for packages.
|
you can browse and search for packages.
|
||||||
|
|
||||||
Any open source project using Composer should publish their packages on
|
Any open source project using Composer is recommended to publish their packages
|
||||||
packagist. A library doesn't need to be on packagist to be used by Composer,
|
on Packagist. A library doesn't need to be on Packagist to be used by Composer,
|
||||||
but it makes life quite a bit simpler.
|
but it enables discovery and adoption by other developers more quickly.
|
||||||
|
|
||||||
## Autoloading
|
## Autoloading
|
||||||
|
|
||||||
For libraries that specify autoload information, Composer generates a
|
For libraries that specify autoload information, Composer generates a
|
||||||
`vendor/autoload.php` file. You can simply include this file and you
|
`vendor/autoload.php` file. You can simply include this file and you will get
|
||||||
will get autoloading for free.
|
autoloading for free.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
require 'vendor/autoload.php';
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
```
|
```
|
||||||
|
|
||||||
This makes it really easy to use third party code. For example: If your
|
This makes it really easy to use third party code. For example: If your project
|
||||||
project depends on monolog, you can just start using classes from it, and they
|
depends on Monolog, you can just start using classes from it, and they will be
|
||||||
will be autoloaded.
|
autoloaded.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$log = new Monolog\Logger('name');
|
$log = new Monolog\Logger('name');
|
||||||
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
|
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
|
||||||
|
|
||||||
$log->addWarning('Foo');
|
$log->addWarning('Foo');
|
||||||
```
|
```
|
||||||
|
|
||||||
You can even add your own code to the autoloader by adding an `autoload` field
|
You can even add your own code to the autoloader by adding an
|
||||||
to `composer.json`.
|
[`autoload`](04-schema.md#autoload) field to `composer.json`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -213,24 +183,25 @@ You define a mapping from namespaces to directories. The `src` directory would
|
||||||
be in your project root, on the same level as `vendor` directory is. An example
|
be in your project root, on the same level as `vendor` directory is. An example
|
||||||
filename would be `src/Foo.php` containing an `Acme\Foo` class.
|
filename would be `src/Foo.php` containing an `Acme\Foo` class.
|
||||||
|
|
||||||
After adding the `autoload` field, you have to re-run `dump-autoload` to re-generate
|
After adding the [`autoload`](04-schema.md#autoload) field, you have to re-run
|
||||||
the `vendor/autoload.php` file.
|
[`dump-autoload`](03-cli.md#dump-autoload) to re-generate the
|
||||||
|
`vendor/autoload.php` file.
|
||||||
|
|
||||||
Including that file will also return the autoloader instance, so you can store
|
Including that file will also return the autoloader instance, so you can store
|
||||||
the return value of the include call in a variable and add more namespaces.
|
the return value of the include call in a variable and add more namespaces.
|
||||||
This can be useful for autoloading classes in a test suite, for example.
|
This can be useful for autoloading classes in a test suite, for example.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$loader = require 'vendor/autoload.php';
|
$loader = require __DIR__ . '/vendor/autoload.php';
|
||||||
$loader->add('Acme\\Test\\', __DIR__);
|
$loader->add('Acme\\Test\\', __DIR__);
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition to PSR-4 autoloading, classmap is also supported. This allows
|
In addition to PSR-4 autoloading, Composer also supports PSR-0, classmap and
|
||||||
classes to be autoloaded even if they do not conform to PSR-4. See the
|
files autoloading. See the [`autoload`](04-schema.md#autoload) reference for
|
||||||
[autoload reference](04-schema.md#autoload) for more details.
|
more information.
|
||||||
|
|
||||||
> **Note:** Composer provides its own autoloader. If you don't want to use
|
> **Note:** Composer provides its own autoloader. If you don't want to use that
|
||||||
that one, you can just include `vendor/composer/autoload_*.php` files,
|
> one, you can just include `vendor/composer/autoload_*.php` files, which return
|
||||||
which return associative arrays allowing you to configure your own autoloader.
|
> associative arrays allowing you to configure your own autoloader.
|
||||||
|
|
||||||
← [Intro](00-intro.md) | [Libraries](02-libraries.md) →
|
← [Intro](00-intro.md) | [Libraries](02-libraries.md) →
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
# Libraries
|
# Libraries
|
||||||
|
|
||||||
This chapter will tell you how to make your library installable through Composer.
|
This chapter will tell you how to make your library installable through
|
||||||
|
Composer.
|
||||||
|
|
||||||
## Every project is a package
|
## Every project is a package
|
||||||
|
|
||||||
As soon as you have a `composer.json` in a directory, that directory is a
|
As soon as you have a `composer.json` in a directory, that directory is a
|
||||||
package. When you add a `require` to a project, you are making a package that
|
package. When you add a [`require`](04-schema.md#require) to a project, you are
|
||||||
depends on other packages. The only difference between your project and
|
making a package that depends on other packages. The only difference between
|
||||||
libraries is that your project is a package without a name.
|
your project and libraries is that your project is a package without a name.
|
||||||
|
|
||||||
In order to make that package installable you need to give it a name. You do
|
In order to make that package installable you need to give it a name. You do
|
||||||
this by adding a `name` to `composer.json`:
|
this by adding the [`name`](04-schema.md#name) property in `composer.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -21,12 +22,12 @@ this by adding a `name` to `composer.json`:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case the project name is `acme/hello-world`, where `acme` is the
|
In this case the project name is `acme/hello-world`, where `acme` is the vendor
|
||||||
vendor name. Supplying a vendor name is mandatory.
|
name. Supplying a vendor name is mandatory.
|
||||||
|
|
||||||
> **Note:** If you don't know what to use as a vendor name, your GitHub
|
> **Note:** If you don't know what to use as a vendor name, your GitHub
|
||||||
username is usually a good bet. While package names are case insensitive, the
|
> username is usually a good bet. While package names are case insensitive, the
|
||||||
convention is all lowercase and dashes for word separation.
|
> convention is all lowercase and dashes for word separation.
|
||||||
|
|
||||||
## Platform packages
|
## Platform packages
|
||||||
|
|
||||||
|
@ -50,15 +51,14 @@ includes PHP itself, PHP extensions and some system libraries.
|
||||||
PHP. The following are available: `curl`, `iconv`, `icu`, `libxml`,
|
PHP. The following are available: `curl`, `iconv`, `icu`, `libxml`,
|
||||||
`openssl`, `pcre`, `uuid`, `xsl`.
|
`openssl`, `pcre`, `uuid`, `xsl`.
|
||||||
|
|
||||||
You can use `composer show --platform` to get a list of your locally available
|
You can use [`show --platform`](03-cli.md#show) to get a list of your locally
|
||||||
platform packages.
|
available platform packages.
|
||||||
|
|
||||||
## Specifying the version
|
## Specifying the version
|
||||||
|
|
||||||
You need to specify the package's version some way. When you publish your
|
When you publish your package on Packagist, it is able to infer the version
|
||||||
package on Packagist, it is able to infer the version from the VCS (git, svn,
|
from the VCS (git, svn, hg) information. This means you don't have to
|
||||||
hg) information, so in that case you do not have to specify it, and it is
|
explicitly declare it. Read [tags](#tags) and [branches](#branches) to see how
|
||||||
recommended not to. See [tags](#tags) and [branches](#branches) to see how
|
|
||||||
version numbers are extracted from these.
|
version numbers are extracted from these.
|
||||||
|
|
||||||
If you are creating packages by hand and really have to specify it explicitly,
|
If you are creating packages by hand and really have to specify it explicitly,
|
||||||
|
@ -76,9 +76,9 @@ you can just add a `version` field:
|
||||||
### Tags
|
### Tags
|
||||||
|
|
||||||
For every tag that looks like a version, a package version of that tag will be
|
For every tag that looks like a version, a package version of that tag will be
|
||||||
created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix
|
created. It should match 'X.Y.Z' or 'vX.Y.Z', with an optional suffix of
|
||||||
of `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffixes
|
`-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffix can also
|
||||||
can also be followed by a number.
|
be followed by a number.
|
||||||
|
|
||||||
Here are a few examples of valid tag names:
|
Here are a few examples of valid tag names:
|
||||||
|
|
||||||
|
@ -89,19 +89,20 @@ Here are a few examples of valid tag names:
|
||||||
- v2.0.0-alpha
|
- v2.0.0-alpha
|
||||||
- v2.0.4-p1
|
- v2.0.4-p1
|
||||||
|
|
||||||
> **Note:** Even if your tag is prefixed with `v`, a [version constraint](01-basic-usage.md#package-versions)
|
> **Note:** Even if your tag is prefixed with `v`, a
|
||||||
> in a `require` statement has to be specified without prefix
|
> [version constraint](01-basic-usage.md#package-versions) in a `require`
|
||||||
> (e.g. tag `v1.0.0` will result in version `1.0.0`).
|
> statement has to be specified without prefix (e.g. tag `v1.0.0` will result
|
||||||
|
> in version `1.0.0`).
|
||||||
|
|
||||||
### Branches
|
### Branches
|
||||||
|
|
||||||
For every branch, a package development version will be created. If the branch
|
For every branch, a package development version will be created. If the branch
|
||||||
name looks like a version, the version will be `{branchname}-dev`. For example
|
name looks like a version, the version will be `{branchname}-dev`. For example,
|
||||||
a branch `2.0` will get a version `2.0.x-dev` (the `.x` is added for technical
|
the branch `2.0` will get the `2.0.x-dev` version (the `.x` is added for
|
||||||
reasons, to make sure it is recognized as a branch, a `2.0.x` branch would also
|
technical reasons, to make sure it is recognized as a branch). The `2.0.x`
|
||||||
be valid and be turned into `2.0.x-dev` as well. If the branch does not look
|
branch would also be valid and be turned into `2.0.x-dev` as well. If the
|
||||||
like a version, it will be `dev-{branchname}`. `master` results in a
|
branch does not look like a version, it will be `dev-{branchname}`. `master`
|
||||||
`dev-master` version.
|
results in a `dev-master` version.
|
||||||
|
|
||||||
Here are some examples of version branch names:
|
Here are some examples of version branch names:
|
||||||
|
|
||||||
|
@ -116,8 +117,8 @@ Here are some examples of version branch names:
|
||||||
### Aliases
|
### Aliases
|
||||||
|
|
||||||
It is possible to alias branch names to versions. For example, you could alias
|
It is possible to alias branch names to versions. For example, you could alias
|
||||||
`dev-master` to `1.0.x-dev`, which would allow you to require `1.0.x-dev` in all
|
`dev-master` to `1.0.x-dev`, which would allow you to require `1.0.x-dev` in
|
||||||
the packages.
|
all the packages.
|
||||||
|
|
||||||
See [Aliases](articles/aliases.md) for more information.
|
See [Aliases](articles/aliases.md) for more information.
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ the `.gitignore`.
|
||||||
|
|
||||||
## Publishing to a VCS
|
## Publishing to a VCS
|
||||||
|
|
||||||
Once you have a vcs repository (version control system, e.g. git) containing a
|
Once you have a VCS repository (version control system, e.g. git) containing a
|
||||||
`composer.json` file, your library is already composer-installable. In this
|
`composer.json` file, your library is already composer-installable. In this
|
||||||
example we will publish the `acme/hello-world` library on GitHub under
|
example we will publish the `acme/hello-world` library on GitHub under
|
||||||
`github.com/username/hello-world`.
|
`github.com/username/hello-world`.
|
||||||
|
@ -180,31 +181,32 @@ For more details on how package repositories work and what other types are
|
||||||
available, see [Repositories](05-repositories.md).
|
available, see [Repositories](05-repositories.md).
|
||||||
|
|
||||||
That's all. You can now install the dependencies by running Composer's
|
That's all. You can now install the dependencies by running Composer's
|
||||||
`install` command!
|
[`install`](03-cli.md#install) command!
|
||||||
|
|
||||||
**Recap:** Any git/svn/hg repository containing a `composer.json` can be added
|
**Recap:** Any git/svn/hg repository containing a `composer.json` can be added
|
||||||
to your project by specifying the package repository and declaring the
|
to your project by specifying the package repository and declaring the
|
||||||
dependency in the `require` field.
|
dependency in the [`require`](04-schema.md#require) field.
|
||||||
|
|
||||||
## Publishing to packagist
|
## Publishing to packagist
|
||||||
|
|
||||||
Alright, so now you can publish packages. But specifying the vcs repository
|
Alright, so now you can publish packages. But specifying the VCS repository
|
||||||
every time is cumbersome. You don't want to force all your users to do that.
|
every time is cumbersome. You don't want to force all your users to do that.
|
||||||
|
|
||||||
The other thing that you may have noticed is that we did not specify a package
|
The other thing that you may have noticed is that we did not specify a package
|
||||||
repository for `monolog/monolog`. How did that work? The answer is packagist.
|
repository for `monolog/monolog`. How did that work? The answer is Packagist.
|
||||||
|
|
||||||
[Packagist](https://packagist.org/) is the main package repository for
|
[Packagist](https://packagist.org/) is the main package repository for
|
||||||
Composer, and it is enabled by default. Anything that is published on
|
Composer, and it is enabled by default. Anything that is published on
|
||||||
packagist is available automatically through Composer. Since monolog
|
Packagist is available automatically through Composer. Since
|
||||||
[is on packagist](https://packagist.org/packages/monolog/monolog), we can depend
|
[Monolog is on Packagist](https://packagist.org/packages/monolog/monolog), we
|
||||||
on it without having to specify any additional repositories.
|
can depend on it without having to specify any additional repositories.
|
||||||
|
|
||||||
If we wanted to share `hello-world` with the world, we would publish it on
|
If we wanted to share `hello-world` with the world, we would publish it on
|
||||||
packagist as well. Doing so is really easy.
|
Packagist as well. Doing so is really easy.
|
||||||
|
|
||||||
You simply hit the big "Submit Package" button and sign up. Then you submit
|
You simply visit [Packagist](https://packagist.org) and hit the "Submit". This
|
||||||
the URL to your VCS repository, at which point packagist will start crawling
|
will prompt you to sign up if you haven't already, and then allows you to
|
||||||
it. Once it is done, your package will be available to anyone.
|
submit the URL to your VCS repository, at which point Packagist will start
|
||||||
|
crawling it. Once it is done, your package will be available to anyone!
|
||||||
|
|
||||||
← [Basic usage](01-basic-usage.md) | [Command-line interface](03-cli.md) →
|
← [Basic usage](01-basic-usage.md) | [Command-line interface](03-cli.md) →
|
||||||
|
|
110
doc/03-cli.md
110
doc/03-cli.md
|
@ -64,30 +64,31 @@ If there is a `composer.lock` file in the current directory, it will use the
|
||||||
exact versions from there instead of resolving them. This ensures that
|
exact versions from there instead of resolving them. This ensures that
|
||||||
everyone using the library will get the same versions of the dependencies.
|
everyone using the library will get the same versions of the dependencies.
|
||||||
|
|
||||||
If there is no `composer.lock` file, composer will create one after dependency
|
If there is no `composer.lock` file, Composer will create one after dependency
|
||||||
resolution.
|
resolution.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--prefer-source:** There are two ways of downloading a package: `source`
|
* **--prefer-source:** There are two ways of downloading a package: `source`
|
||||||
and `dist`. For stable versions composer will use the `dist` by default.
|
and `dist`. For stable versions Composer will use the `dist` by default.
|
||||||
The `source` is a version control repository. If `--prefer-source` is
|
The `source` is a version control repository. If `--prefer-source` is
|
||||||
enabled, composer will install from `source` if there is one. This is
|
enabled, Composer will install from `source` if there is one. This is
|
||||||
useful if you want to make a bugfix to a project and get a local git
|
useful if you want to make a bugfix to a project and get a local git
|
||||||
clone of the dependency directly.
|
clone of the dependency directly.
|
||||||
* **--prefer-dist:** Reverse of `--prefer-source`, composer will install
|
* **--prefer-dist:** Reverse of `--prefer-source`, Composer will install
|
||||||
from `dist` if possible. This can speed up installs substantially on build
|
from `dist` if possible. This can speed up installs substantially on build
|
||||||
servers and other use cases where you typically do not run updates of the
|
servers and other use cases where you typically do not run updates of the
|
||||||
vendors. It is also a way to circumvent problems with git if you do not
|
vendors. It is also a way to circumvent problems with git if you do not
|
||||||
have a proper setup.
|
have a proper setup.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the [`platform`](06-config.md#platform) config option.
|
||||||
* **--dry-run:** If you want to run through an installation without actually
|
* **--dry-run:** If you want to run through an installation without actually
|
||||||
installing a package, you can use `--dry-run`. This will simulate the
|
installing a package, you can use `--dry-run`. This will simulate the
|
||||||
installation and show you what would happen.
|
installation and show you what would happen.
|
||||||
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
* **--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-dev:** Skip installing packages listed in `require-dev`. The autoloader
|
||||||
|
generation skips the `autoload-dev` rules.
|
||||||
* **--no-autoloader:** Skips autoloader generation.
|
* **--no-autoloader:** Skips autoloader generation.
|
||||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||||
* **--no-plugins:** Disables plugins.
|
* **--no-plugins:** Disables plugins.
|
||||||
|
@ -96,6 +97,8 @@ resolution.
|
||||||
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||||
autoloader. This is recommended especially for production, but can take
|
autoloader. This is recommended especially for production, but can take
|
||||||
a bit of time to run so it is currently not done by default.
|
a bit of time to run so it is currently not done by default.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize-autoloader`.
|
||||||
|
|
||||||
## update
|
## update
|
||||||
|
|
||||||
|
@ -127,7 +130,7 @@ php composer.phar update vendor/*
|
||||||
* **--prefer-dist:** Install packages from `dist` when available.
|
* **--prefer-dist:** Install packages from `dist` when available.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the [`platform`](06-config.md#platform) config option.
|
||||||
* **--dry-run:** Simulate the command without actually doing anything.
|
* **--dry-run:** Simulate the command without actually doing anything.
|
||||||
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
* **--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-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
|
||||||
|
@ -139,9 +142,11 @@ php composer.phar update vendor/*
|
||||||
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||||
autoloader. This is recommended especially for production, but can take
|
autoloader. This is recommended especially for production, but can take
|
||||||
a bit of time to run so it is currently not done by default.
|
a bit of time to run so it is currently not done by default.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize-autoloader`.
|
||||||
* **--lock:** Only updates the lock file hash to suppress warning about the
|
* **--lock:** Only updates the lock file hash to suppress warning about the
|
||||||
lock file being out of date.
|
lock file being out of date.
|
||||||
* **--with-dependencies** Add also all dependencies of whitelisted packages to the whitelist.
|
* **--with-dependencies:** Add also all dependencies of whitelisted packages to the whitelist.
|
||||||
* **--prefer-stable:** Prefer stable versions of dependencies.
|
* **--prefer-stable:** Prefer stable versions of dependencies.
|
||||||
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
|
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
|
||||||
versions of requirements, generally used with `--prefer-stable`.
|
versions of requirements, generally used with `--prefer-stable`.
|
||||||
|
@ -171,14 +176,20 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master
|
||||||
* **--prefer-dist:** Install packages from `dist` when available.
|
* **--prefer-dist:** Install packages from `dist` when available.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the [`platform`](06-config.md#platform) config option.
|
||||||
* **--dev:** Add packages to `require-dev`.
|
* **--dev:** Add packages to `require-dev`.
|
||||||
* **--no-update:** Disables the automatic update of the dependencies.
|
* **--no-update:** Disables the automatic update of the dependencies.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
terminals or scripts which don't handle backspace characters.
|
terminals or scripts which don't handle backspace characters.
|
||||||
* **--update-no-dev** Run the dependency update with the --no-dev option.
|
* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
|
||||||
* **--update-with-dependencies** Also update dependencies of the newly
|
* **--update-with-dependencies:** Also update dependencies of the newly
|
||||||
required packages.
|
required packages.
|
||||||
|
* **--sort-packages:** Keep packages sorted in `composer.json`.
|
||||||
|
* **--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.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize-autoloader`.
|
||||||
|
|
||||||
## remove
|
## remove
|
||||||
|
|
||||||
|
@ -195,13 +206,18 @@ uninstalled.
|
||||||
### Options
|
### Options
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the [`platform`](06-config.md#platform) config option.
|
||||||
* **--dev:** Remove packages from `require-dev`.
|
* **--dev:** Remove packages from `require-dev`.
|
||||||
* **--no-update:** Disables the automatic update of the dependencies.
|
* **--no-update:** Disables the automatic update of the dependencies.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
terminals or scripts which don't handle backspace characters.
|
terminals or scripts which don't handle backspace characters.
|
||||||
* **--update-no-dev** Run the dependency update with the --no-dev option.
|
* **--update-no-dev:** Run the dependency update with the --no-dev option.
|
||||||
* **--update-with-dependencies** Also update dependencies of the removed packages.
|
* **--update-with-dependencies:** Also update dependencies of the removed packages.
|
||||||
|
* **--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.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize-autoloader`.
|
||||||
|
|
||||||
## global
|
## global
|
||||||
|
|
||||||
|
@ -259,8 +275,8 @@ name : monolog/monolog
|
||||||
versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
|
versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
|
||||||
type : library
|
type : library
|
||||||
names : monolog/monolog
|
names : monolog/monolog
|
||||||
source : [git] http://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
source : [git] https://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
||||||
dist : [zip] http://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
dist : [zip] https://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
||||||
license : MIT
|
license : MIT
|
||||||
|
|
||||||
autoload
|
autoload
|
||||||
|
@ -293,6 +309,18 @@ in your browser.
|
||||||
|
|
||||||
* **--homepage (-H):** Open the homepage instead of the repository URL.
|
* **--homepage (-H):** Open the homepage instead of the repository URL.
|
||||||
|
|
||||||
|
## suggests
|
||||||
|
|
||||||
|
Lists all packages suggested by currently installed set of packages. You can
|
||||||
|
optionally pass one or multiple package names in the format of `vendor/package`
|
||||||
|
to limit output to suggestions made by those packages only.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--no-dev:** Excludes suggestions from `require-dev` packages.
|
||||||
|
* **--verbose (-v):** Increased verbosity adds suggesting package name and
|
||||||
|
reason for suggestion.
|
||||||
|
|
||||||
## depends
|
## depends
|
||||||
|
|
||||||
The `depends` command tells you which other packages depend on a certain
|
The `depends` command tells you which other packages depend on a certain
|
||||||
|
@ -326,7 +354,9 @@ php composer.phar validate
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--no-check-all:** Whether or not composer do a complete validation.
|
* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound version constraints.
|
||||||
|
* **--no-check-lock:** Do not emit an error if `composer.lock` exists and is not up to date.
|
||||||
|
* **--no-check-publish:** Do not emit an error if `composer.json` is unsuitable for publishing as a package on Packagist but is otherwise valid.
|
||||||
|
|
||||||
## status
|
## status
|
||||||
|
|
||||||
|
@ -351,7 +381,7 @@ vendor/seld/jsonlint:
|
||||||
|
|
||||||
## self-update
|
## self-update
|
||||||
|
|
||||||
To update composer itself to the latest version, just run the `self-update`
|
To update Composer itself to the latest version, just run the `self-update`
|
||||||
command. It will replace your `composer.phar` with the latest version.
|
command. It will replace your `composer.phar` with the latest version.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -364,7 +394,7 @@ If you would like to instead update to a specific release simply specify it:
|
||||||
php composer.phar self-update 1.0.0-alpha7
|
php composer.phar self-update 1.0.0-alpha7
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have installed composer for your entire system (see [global installation](00-intro.md#globally)),
|
If you have installed Composer for your entire system (see [global installation](00-intro.md#globally)),
|
||||||
you may have to run the command with `root` privileges
|
you may have to run the command with `root` privileges
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -374,11 +404,12 @@ sudo composer self-update
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--rollback (-r):** Rollback to the last version you had installed.
|
* **--rollback (-r):** Rollback to the last version you had installed.
|
||||||
* **--clean-backups:** Delete old backups during an update. This makes the current version of composer the only backup available after the update.
|
* **--clean-backups:** Delete old backups during an update. This makes the
|
||||||
|
current version of Composer the only backup available after the update.
|
||||||
|
|
||||||
## config
|
## config
|
||||||
|
|
||||||
The `config` command allows you to edit some basic composer settings in either
|
The `config` command allows you to edit some basic Composer settings in either
|
||||||
the local composer.json file or the global config.json file.
|
the local composer.json file or the global config.json file.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -393,8 +424,7 @@ php composer.phar config --list
|
||||||
configuration value. For settings that can take an array of values (like
|
configuration value. For settings that can take an array of values (like
|
||||||
`github-protocols`), more than one setting-value arguments are allowed.
|
`github-protocols`), more than one setting-value arguments are allowed.
|
||||||
|
|
||||||
See the [config schema section](04-schema.md#config) for valid configuration
|
See the [Config](06-config.md) chapter for valid configuration options.
|
||||||
options.
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
|
@ -418,13 +448,13 @@ In addition to modifying the config section, the `config` command also supports
|
||||||
changes to the repositories section by using it the following way:
|
changes to the repositories section by using it the following way:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
php composer.phar config repositories.foo vcs http://github.com/foo/bar
|
php composer.phar config repositories.foo vcs https://github.com/foo/bar
|
||||||
```
|
```
|
||||||
|
|
||||||
## create-project
|
## create-project
|
||||||
|
|
||||||
You can use Composer to create new projects from an existing package. This is
|
You can use Composer to create new projects from an existing package. This is
|
||||||
the equivalent of doing a git clone/svn checkout followed by a composer install
|
the equivalent of doing a git clone/svn checkout followed by a "composer install"
|
||||||
of the vendors.
|
of the vendors.
|
||||||
|
|
||||||
There are several applications for this:
|
There are several applications for this:
|
||||||
|
@ -434,7 +464,7 @@ There are several applications for this:
|
||||||
3. Projects with multiple developers can use this feature to bootstrap the
|
3. Projects with multiple developers can use this feature to bootstrap the
|
||||||
initial application for development.
|
initial application for development.
|
||||||
|
|
||||||
To create a new project using composer you can use the "create-project" command.
|
To create a new project using Composer you can use the "create-project" command.
|
||||||
Pass it a package name, and the directory to create the project in. You can also
|
Pass it a package name, and the directory to create the project in. You can also
|
||||||
provide a version as third argument, otherwise the latest version is used.
|
provide a version as third argument, otherwise the latest version is used.
|
||||||
|
|
||||||
|
@ -489,6 +519,8 @@ performance.
|
||||||
* **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
* **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||||
autoloader. This is recommended especially for production, but can take
|
autoloader. This is recommended especially for production, but can take
|
||||||
a bit of time to run so it is currently not done by default.
|
a bit of time to run so it is currently not done by default.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize`.
|
||||||
* **--no-dev:** Disables autoload-dev rules.
|
* **--no-dev:** Disables autoload-dev rules.
|
||||||
|
|
||||||
## clear-cache
|
## clear-cache
|
||||||
|
@ -567,6 +599,8 @@ For example:
|
||||||
COMPOSER=composer-other.json php composer.phar install
|
COMPOSER=composer-other.json php composer.phar install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The generated lock file will use the same name: `composer-other.lock` in this example.
|
||||||
|
|
||||||
### COMPOSER_ROOT_VERSION
|
### COMPOSER_ROOT_VERSION
|
||||||
|
|
||||||
By setting this var you can specify the version of the root package, if it can
|
By setting this var you can specify the version of the root package, if it can
|
||||||
|
@ -574,7 +608,7 @@ not be guessed from VCS info and is not present in `composer.json`.
|
||||||
|
|
||||||
### COMPOSER_VENDOR_DIR
|
### COMPOSER_VENDOR_DIR
|
||||||
|
|
||||||
By setting this var you can make composer install the dependencies into a
|
By setting this var you can make Composer install the dependencies into a
|
||||||
directory other than `vendor`.
|
directory other than `vendor`.
|
||||||
|
|
||||||
### COMPOSER_BIN_DIR
|
### COMPOSER_BIN_DIR
|
||||||
|
@ -584,7 +618,7 @@ directory to something other than `vendor/bin`.
|
||||||
|
|
||||||
### http_proxy or HTTP_PROXY
|
### http_proxy or HTTP_PROXY
|
||||||
|
|
||||||
If you are using composer from behind an HTTP proxy, you can use the standard
|
If you are using Composer from behind an HTTP proxy, you can use the standard
|
||||||
`http_proxy` or `HTTP_PROXY` env vars. Simply set it to the URL of your proxy.
|
`http_proxy` or `HTTP_PROXY` env vars. Simply set it to the URL of your proxy.
|
||||||
Many operating systems already set this variable for you.
|
Many operating systems already set this variable for you.
|
||||||
|
|
||||||
|
@ -606,18 +640,18 @@ can also set it to `*` to ignore the proxy for all HTTP requests.
|
||||||
### HTTP_PROXY_REQUEST_FULLURI
|
### HTTP_PROXY_REQUEST_FULLURI
|
||||||
|
|
||||||
If you use a proxy but it does not support the request_fulluri flag, then you
|
If you use a proxy but it does not support the request_fulluri flag, then you
|
||||||
should set this env var to `false` or `0` to prevent composer from setting the
|
should set this env var to `false` or `0` to prevent Composer from setting the
|
||||||
request_fulluri option.
|
request_fulluri option.
|
||||||
|
|
||||||
### HTTPS_PROXY_REQUEST_FULLURI
|
### HTTPS_PROXY_REQUEST_FULLURI
|
||||||
|
|
||||||
If you use a proxy but it does not support the request_fulluri flag for HTTPS
|
If you use a proxy but it does not support the request_fulluri flag for HTTPS
|
||||||
requests, then you should set this env var to `false` or `0` to prevent composer
|
requests, then you should set this env var to `false` or `0` to prevent Composer
|
||||||
from setting the request_fulluri option.
|
from setting the request_fulluri option.
|
||||||
|
|
||||||
### COMPOSER_HOME
|
### COMPOSER_HOME
|
||||||
|
|
||||||
The `COMPOSER_HOME` var allows you to change the composer home directory. This
|
The `COMPOSER_HOME` var allows you to change the Composer home directory. This
|
||||||
is a hidden, global (per-user on the machine) directory that is shared between
|
is a hidden, global (per-user on the machine) directory that is shared between
|
||||||
all projects.
|
all projects.
|
||||||
|
|
||||||
|
@ -631,32 +665,32 @@ You may put a `config.json` file into the location which `COMPOSER_HOME` points
|
||||||
to. Composer will merge this configuration with your project's `composer.json`
|
to. Composer will merge this configuration with your project's `composer.json`
|
||||||
when you run the `install` and `update` commands.
|
when you run the `install` and `update` commands.
|
||||||
|
|
||||||
This file allows you to set [configuration](04-schema.md#config) and
|
This file allows you to set [repositories](05-repositories.md) and
|
||||||
[repositories](05-repositories.md) for the user's projects.
|
[configuration](06-config.md) for the user's projects.
|
||||||
|
|
||||||
In case global configuration matches _local_ configuration, the _local_
|
In case global configuration matches _local_ configuration, the _local_
|
||||||
configuration in the project's `composer.json` always wins.
|
configuration in the project's `composer.json` always wins.
|
||||||
|
|
||||||
### COMPOSER_CACHE_DIR
|
### COMPOSER_CACHE_DIR
|
||||||
|
|
||||||
The `COMPOSER_CACHE_DIR` var allows you to change the composer cache directory,
|
The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory,
|
||||||
which is also configurable via the [`cache-dir`](04-schema.md#config) option.
|
which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option.
|
||||||
|
|
||||||
By default it points to $COMPOSER_HOME/cache on \*nix and OSX, and
|
By default it points to $COMPOSER_HOME/cache on \*nix and OSX, and
|
||||||
`C:\Users\<user>\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows.
|
`C:\Users\<user>\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows.
|
||||||
|
|
||||||
### COMPOSER_PROCESS_TIMEOUT
|
### COMPOSER_PROCESS_TIMEOUT
|
||||||
|
|
||||||
This env var controls the time composer waits for commands (such as git
|
This env var controls the time Composer waits for commands (such as git
|
||||||
commands) to finish executing. The default value is 300 seconds (5 minutes).
|
commands) to finish executing. The default value is 300 seconds (5 minutes).
|
||||||
|
|
||||||
### COMPOSER_DISCARD_CHANGES
|
### COMPOSER_DISCARD_CHANGES
|
||||||
|
|
||||||
This env var controls the discard-changes [config option](04-schema.md#config).
|
This env var controls the [`discard-changes`](06-config.md#discard-changes) config option.
|
||||||
|
|
||||||
### COMPOSER_NO_INTERACTION
|
### COMPOSER_NO_INTERACTION
|
||||||
|
|
||||||
If set to 1, this env var will make composer behave as if you passed the
|
If set to 1, this env var will make Composer behave as if you passed the
|
||||||
`--no-interaction` flag to every command. This can be set on build boxes/CI.
|
`--no-interaction` flag to every command. This can be set on build boxes/CI.
|
||||||
|
|
||||||
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
||||||
|
|
168
doc/04-schema.md
168
doc/04-schema.md
|
@ -20,9 +20,6 @@ this is the `config` field. Only the root package can define configuration.
|
||||||
The config of dependencies is ignored. This makes the `config` field
|
The config of dependencies is ignored. This makes the `config` field
|
||||||
`root-only`.
|
`root-only`.
|
||||||
|
|
||||||
If you clone one of those dependencies to work on it, then that package is the
|
|
||||||
root package. The `composer.json` is identical, but the context is different.
|
|
||||||
|
|
||||||
> **Note:** A package can be the root package or not, depending on the context.
|
> **Note:** A package can be the root package or not, depending on the context.
|
||||||
> For example, if your project depends on the `monolog` library, your project
|
> For example, if your project depends on the `monolog` library, your project
|
||||||
> is the root package. However, if you clone `monolog` from GitHub in order to
|
> is the root package. However, if you clone `monolog` from GitHub in order to
|
||||||
|
@ -87,7 +84,7 @@ that needs some special logic, you can define a custom type. This could be a
|
||||||
all be specific to certain projects, and they will need to provide an
|
all be specific to certain projects, and they will need to provide an
|
||||||
installer capable of installing packages of that type.
|
installer capable of installing packages of that type.
|
||||||
|
|
||||||
Out of the box, composer supports four types:
|
Out of the box, Composer supports four types:
|
||||||
|
|
||||||
- **library:** This is the default. It will simply copy the files to `vendor`.
|
- **library:** This is the default. It will simply copy the files to `vendor`.
|
||||||
- **project:** This denotes a project rather than a library. For example
|
- **project:** This denotes a project rather than a library. For example
|
||||||
|
@ -157,7 +154,7 @@ The recommended notation for the most common licenses is (alphabetical):
|
||||||
- MIT
|
- MIT
|
||||||
|
|
||||||
Optional, but it is highly recommended to supply this. More identifiers are
|
Optional, but it is highly recommended to supply this. More identifiers are
|
||||||
listed at the [SPDX Open Source License Registry](http://www.spdx.org/licenses/).
|
listed at the [SPDX Open Source License Registry](https://www.spdx.org/licenses/).
|
||||||
|
|
||||||
For closed-source software, you may use `"proprietary"` as the license identifier.
|
For closed-source software, you may use `"proprietary"` as the license identifier.
|
||||||
|
|
||||||
|
@ -235,11 +232,12 @@ Various information to get support about the project.
|
||||||
Support information includes the following:
|
Support information includes the following:
|
||||||
|
|
||||||
* **email:** Email address for support.
|
* **email:** Email address for support.
|
||||||
* **issues:** URL to the Issue Tracker.
|
* **issues:** URL to the issue tracker.
|
||||||
* **forum:** URL to the Forum.
|
* **forum:** URL to the forum.
|
||||||
* **wiki:** URL to the Wiki.
|
* **wiki:** URL to the wiki.
|
||||||
* **irc:** IRC channel for support, as irc://server/channel.
|
* **irc:** IRC channel for support, as irc://server/channel.
|
||||||
* **source:** URL to browse or download the sources.
|
* **source:** URL to browse or download the sources.
|
||||||
|
* **docs:** URL to the documentation.
|
||||||
|
|
||||||
An example:
|
An example:
|
||||||
|
|
||||||
|
@ -271,7 +269,7 @@ Example:
|
||||||
|
|
||||||
All links are optional fields.
|
All links are optional fields.
|
||||||
|
|
||||||
`require` and `require-dev` additionally support stability flags (root-only).
|
`require` and `require-dev` additionally support stability flags ([root-only](04-schema.md#root-package)).
|
||||||
These allow you to further restrict or expand the stability of a package beyond
|
These allow you to further restrict or expand the stability of a package beyond
|
||||||
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
|
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
|
||||||
them to a constraint, or just apply them to an empty constraint if you want to
|
them to a constraint, or just apply them to an empty constraint if you want to
|
||||||
|
@ -334,7 +332,7 @@ aliases article](articles/aliases.md).
|
||||||
Lists packages required by this package. The package will not be installed
|
Lists packages required by this package. The package will not be installed
|
||||||
unless those requirements can be met.
|
unless those requirements can be met.
|
||||||
|
|
||||||
#### require-dev <span>(root-only)</span>
|
#### require-dev <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
Lists packages required for developing this package, or running
|
Lists packages required for developing this package, or running
|
||||||
tests, etc. The dev requirements of the root package are installed by default.
|
tests, etc. The dev requirements of the root package are installed by default.
|
||||||
|
@ -554,7 +552,27 @@ Example:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### autoload-dev <span>(root-only)</span>
|
#### Exclude files from classmaps
|
||||||
|
|
||||||
|
If you want to exclude some files or folders from the classmap you can use the 'exclude-from-classmap' property.
|
||||||
|
This might be useful to exclude test classes in your live environment, for example, as those will be skipped
|
||||||
|
from the classmap even when building an optimized autoloader.
|
||||||
|
|
||||||
|
The classmap generator will ignore all files in the paths configured here. The paths are absolute from the package
|
||||||
|
root directory (i.e. composer.json location), and support `*` to match anything but a slash, and `**` to
|
||||||
|
match anything. `**` is implicitly added to the end of the paths.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"exclude-from-classmap": ["/Tests/", "/test/", "/tests/"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### autoload-dev <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
This section allows to define autoload rules for development purposes.
|
This section allows to define autoload rules for development purposes.
|
||||||
|
|
||||||
|
@ -627,7 +645,7 @@ To do that, `autoload` and `target-dir` are defined as follows:
|
||||||
|
|
||||||
Optional.
|
Optional.
|
||||||
|
|
||||||
### minimum-stability <span>(root-only)</span>
|
### minimum-stability <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
This defines the default behavior for filtering packages by stability. This
|
This defines the default behavior for filtering packages by stability. This
|
||||||
defaults to `stable`, so if you rely on a `dev` package, you should specify
|
defaults to `stable`, so if you rely on a `dev` package, you should specify
|
||||||
|
@ -642,7 +660,7 @@ a given package can be done in `require` or `require-dev` (see
|
||||||
Available options (in order of stability) are `dev`, `alpha`, `beta`, `RC`,
|
Available options (in order of stability) are `dev`, `alpha`, `beta`, `RC`,
|
||||||
and `stable`.
|
and `stable`.
|
||||||
|
|
||||||
### prefer-stable <span>(root-only)</span>
|
### prefer-stable <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
When this is enabled, Composer will prefer more stable packages over unstable
|
When this is enabled, Composer will prefer more stable packages over unstable
|
||||||
ones when finding compatible stable packages is possible. If you require a
|
ones when finding compatible stable packages is possible. If you require a
|
||||||
|
@ -651,11 +669,11 @@ selected granted that the minimum-stability allows for it.
|
||||||
|
|
||||||
Use `"prefer-stable": true` to enable.
|
Use `"prefer-stable": true` to enable.
|
||||||
|
|
||||||
### repositories <span>(root-only)</span>
|
### repositories <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
Custom package repositories to use.
|
Custom package repositories to use.
|
||||||
|
|
||||||
By default composer just uses the packagist repository. By specifying
|
By default Composer just uses the packagist repository. By specifying
|
||||||
repositories you can get packages from elsewhere.
|
repositories you can get packages from elsewhere.
|
||||||
|
|
||||||
Repositories are not resolved recursively. You can only add them to your main
|
Repositories are not resolved recursively. You can only add them to your main
|
||||||
|
@ -664,14 +682,14 @@ ignored.
|
||||||
|
|
||||||
The following repository types are supported:
|
The following repository types are supported:
|
||||||
|
|
||||||
* **composer:** A composer repository is simply a `packages.json` file served
|
* **composer:** A Composer repository is simply a `packages.json` file served
|
||||||
via the network (HTTP, FTP, SSH), that contains a list of `composer.json`
|
via the network (HTTP, FTP, SSH), that contains a list of `composer.json`
|
||||||
objects with additional `dist` and/or `source` information. The `packages.json`
|
objects with additional `dist` and/or `source` information. The `packages.json`
|
||||||
file is loaded using a PHP stream. You can set extra options on that stream
|
file is loaded using a PHP stream. You can set extra options on that stream
|
||||||
using the `options` parameter.
|
using the `options` parameter.
|
||||||
* **vcs:** The version control system repository can fetch packages from git,
|
* **vcs:** The version control system repository can fetch packages from git,
|
||||||
svn and hg repositories.
|
svn and hg repositories.
|
||||||
* **pear:** With this you can import any pear repository into your composer
|
* **pear:** With this you can import any pear repository into your Composer
|
||||||
project.
|
project.
|
||||||
* **package:** If you depend on a project that does not have any support for
|
* **package:** If you depend on a project that does not have any support for
|
||||||
composer whatsoever you can define the package inline using a `package`
|
composer whatsoever you can define the package inline using a `package`
|
||||||
|
@ -703,7 +721,7 @@ Example:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "pear",
|
"type": "pear",
|
||||||
"url": "http://pear2.php.net"
|
"url": "https://pear2.php.net"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "package",
|
"type": "package",
|
||||||
|
@ -715,7 +733,7 @@ Example:
|
||||||
"type": "zip"
|
"type": "zip"
|
||||||
},
|
},
|
||||||
"source": {
|
"source": {
|
||||||
"url": "http://smarty-php.googlecode.com/svn/",
|
"url": "https://smarty-php.googlecode.com/svn/",
|
||||||
"type": "svn",
|
"type": "svn",
|
||||||
"reference": "tags/Smarty_3_1_7/distribution/"
|
"reference": "tags/Smarty_3_1_7/distribution/"
|
||||||
}
|
}
|
||||||
|
@ -730,101 +748,12 @@ will look from the first to the last repository, and pick the first match.
|
||||||
By default Packagist is added last which means that custom repositories can
|
By default Packagist is added last which means that custom repositories can
|
||||||
override packages from it.
|
override packages from it.
|
||||||
|
|
||||||
### config <span>(root-only)</span>
|
### config <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
A set of configuration options. It is only used for projects.
|
A set of configuration options. It is only used for projects. See
|
||||||
|
[Config](06-config.md) for a description of each individual option.
|
||||||
|
|
||||||
The following options are supported:
|
### scripts <span>([root-only](04-schema.md#root-package))</span>
|
||||||
|
|
||||||
* **process-timeout:** Defaults to `300`. The duration processes like git clones
|
|
||||||
can run before Composer assumes they died out. You may need to make this
|
|
||||||
higher if you have a slow connection or huge vendors.
|
|
||||||
* **use-include-path:** Defaults to `false`. If true, the Composer autoloader
|
|
||||||
will also look for classes in the PHP include path.
|
|
||||||
* **preferred-install:** Defaults to `auto` and can be any of `source`, `dist` or
|
|
||||||
`auto`. This option allows you to set the install method Composer will prefer to
|
|
||||||
use.
|
|
||||||
* **store-auths:** What to do after prompting for authentication, one of:
|
|
||||||
`true` (always store), `false` (do not store) and `"prompt"` (ask every
|
|
||||||
time), defaults to `"prompt"`.
|
|
||||||
* **github-protocols:** Defaults to `["git", "https", "ssh"]`. A list of protocols to
|
|
||||||
use when cloning from github.com, in priority order. You can reconfigure it to
|
|
||||||
for example prioritize the https protocol if you are behind a proxy or have somehow
|
|
||||||
bad performances with the git protocol.
|
|
||||||
* **github-oauth:** A list of domain names and oauth keys. For example using
|
|
||||||
`{"github.com": "oauthtoken"}` as the value of this option will use `oauthtoken`
|
|
||||||
to access private repositories on github and to circumvent the low IP-based
|
|
||||||
rate limiting of their API.
|
|
||||||
[Read more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)
|
|
||||||
on how to get an OAuth token for GitHub.
|
|
||||||
* **http-basic:** A list of domain names and username/passwords to authenticate
|
|
||||||
against them. For example using
|
|
||||||
`{"example.org": {"username": "alice", "password": "foo"}` as the value of this
|
|
||||||
option will let composer authenticate against example.org.
|
|
||||||
* **vendor-dir:** Defaults to `vendor`. You can install dependencies into a
|
|
||||||
different directory if you want to. `$HOME` and `~` will be replaced by your
|
|
||||||
home directory's path in vendor-dir and all `*-dir` options below.
|
|
||||||
* **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they
|
|
||||||
will be symlinked into this directory.
|
|
||||||
* **cache-dir:** Defaults to `$COMPOSER_HOME/cache` on unix systems and
|
|
||||||
`C:\Users\<user>\AppData\Local\Composer` on Windows. Stores all the caches
|
|
||||||
used by composer. See also [COMPOSER_HOME](03-cli.md#composer-home).
|
|
||||||
* **cache-files-dir:** Defaults to `$cache-dir/files`. Stores the zip archives
|
|
||||||
of packages.
|
|
||||||
* **cache-repo-dir:** Defaults to `$cache-dir/repo`. Stores repository metadata
|
|
||||||
for the `composer` type and the VCS repos of type `svn`, `github` and `bitbucket`.
|
|
||||||
* **cache-vcs-dir:** Defaults to `$cache-dir/vcs`. Stores VCS clones for
|
|
||||||
loading VCS repository metadata for the `git`/`hg` types and to speed up installs.
|
|
||||||
* **cache-files-ttl:** Defaults to `15552000` (6 months). Composer caches all
|
|
||||||
dist (zip, tar, ..) packages that it downloads. Those are purged after six
|
|
||||||
months of being unused by default. This option allows you to tweak this
|
|
||||||
duration (in seconds) or disable it completely by setting it to 0.
|
|
||||||
* **cache-files-maxsize:** Defaults to `300MiB`. Composer caches all
|
|
||||||
dist (zip, tar, ..) packages that it downloads. When the garbage collection
|
|
||||||
is periodically ran, this is the maximum size the cache will be able to use.
|
|
||||||
Older (less used) files will be removed first until the cache fits.
|
|
||||||
* **prepend-autoloader:** Defaults to `true`. If false, the composer autoloader
|
|
||||||
will not be prepended to existing autoloaders. This is sometimes required to fix
|
|
||||||
interoperability issues with other autoloaders.
|
|
||||||
* **autoloader-suffix:** Defaults to `null`. String to be used as a suffix for
|
|
||||||
the generated Composer autoloader. When null a random one will be generated.
|
|
||||||
* **optimize-autoloader** Defaults to `false`. Always optimize when dumping
|
|
||||||
the autoloader.
|
|
||||||
* **classmap-authoritative:** Defaults to `false`. If true, the composer
|
|
||||||
autoloader will not scan the filesystem for classes that are not found in
|
|
||||||
the class map. Implies 'optimize-autoloader'.
|
|
||||||
* **github-domains:** Defaults to `["github.com"]`. A list of domains to use in
|
|
||||||
github mode. This is used for GitHub Enterprise setups.
|
|
||||||
* **github-expose-hostname:** Defaults to `true`. If set to false, the OAuth
|
|
||||||
tokens created to access the github API will have a date instead of the
|
|
||||||
machine hostname.
|
|
||||||
* **gitlab-domains:** Defaults to `["gitlab.com"]`. A list of domains of
|
|
||||||
GitLab servers. This is used if you use the `gitlab` repository type.
|
|
||||||
* **notify-on-install:** Defaults to `true`. Composer allows repositories to
|
|
||||||
define a notification URL, so that they get notified whenever a package from
|
|
||||||
that repository is installed. This option allows you to disable that behaviour.
|
|
||||||
* **discard-changes:** Defaults to `false` and can be any of `true`, `false` or
|
|
||||||
`"stash"`. This option allows you to set the default style of handling dirty
|
|
||||||
updates when in non-interactive mode. `true` will always discard changes in
|
|
||||||
vendors, while `"stash"` will try to stash and reapply. Use this for CI
|
|
||||||
servers or deploy scripts if you tend to have modified vendors.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"bin-dir": "bin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note:** Authentication-related config options like `http-basic` and
|
|
||||||
> `github-oauth` can also be specified inside a `auth.json` file that goes
|
|
||||||
> besides your `composer.json`. That way you can gitignore it and every
|
|
||||||
> developer can place their own credentials in there.
|
|
||||||
|
|
||||||
### scripts <span>(root-only)</span>
|
|
||||||
|
|
||||||
Composer allows you to hook into various parts of the installation process
|
Composer allows you to hook into various parts of the installation process
|
||||||
through the use of scripts.
|
through the use of scripts.
|
||||||
|
@ -882,10 +811,11 @@ Optional.
|
||||||
|
|
||||||
### non-feature-branches
|
### non-feature-branches
|
||||||
|
|
||||||
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something), that will NOT be handled as feature branches. This is an array of string.
|
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something),
|
||||||
|
that will NOT be handled as feature branches. This is an array of strings.
|
||||||
|
|
||||||
If you have non-numeric branch names, for example like "latest", "current", "latest-stable"
|
If you have non-numeric branch names, for example like "latest", "current", "latest-stable"
|
||||||
or something, that do not look like a version number, then composer handles such branches
|
or something, that do not look like a version number, then Composer handles such branches
|
||||||
as feature branches. This means it searches for parent branches, that look like a version
|
as feature branches. This means it searches for parent branches, that look like a version
|
||||||
or ends at special branches (like master) and the root package version number becomes the
|
or ends at special branches (like master) and the root package version number becomes the
|
||||||
version of the parent branch or at least master or something.
|
version of the parent branch or at least master or something.
|
||||||
|
@ -902,11 +832,13 @@ An example:
|
||||||
If you have a testing branch, that is heavily maintained during a testing phase and is
|
If you have a testing branch, that is heavily maintained during a testing phase and is
|
||||||
deployed to your staging environment, normally "composer show -s" will give you `versions : * dev-master`.
|
deployed to your staging environment, normally "composer show -s" will give you `versions : * dev-master`.
|
||||||
|
|
||||||
If you configure latest-.* as a pattern for non-feature-branches like this:
|
If you configure `latest-.*` as a pattern for non-feature-branches like this:
|
||||||
|
|
||||||
{
|
```json
|
||||||
"non-feature-branches": ["latest-.*"]
|
{
|
||||||
}
|
"non-feature-branches": ["latest-.*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Then "composer show -s" will give you `versions : * dev-latest-testing`.
|
Then "composer show -s" will give you `versions : * dev-latest-testing`.
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ of repositories are available, and how they work.
|
||||||
## Concepts
|
## Concepts
|
||||||
|
|
||||||
Before we look at the different types of repositories that exist, we need to
|
Before we look at the different types of repositories that exist, we need to
|
||||||
understand some of the basic concepts that composer is built on.
|
understand some of the basic concepts that Composer is built on.
|
||||||
|
|
||||||
### Package
|
### Package
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ code, but in theory it could be anything. And it contains a package
|
||||||
description which has a name and a version. The name and the version are used
|
description which has a name and a version. The name and the version are used
|
||||||
to identify the package.
|
to identify the package.
|
||||||
|
|
||||||
In fact, internally composer sees every version as a separate package. While
|
In fact, internally Composer sees every version as a separate package. While
|
||||||
this distinction does not matter when you are using composer, it's quite
|
this distinction does not matter when you are using Composer, it's quite
|
||||||
important when you want to change it.
|
important when you want to change it.
|
||||||
|
|
||||||
In addition to the name and the version, there is useful metadata. The information
|
In addition to the name and the version, there is useful metadata. The information
|
||||||
|
@ -103,7 +103,7 @@ It may include any of the other fields specified in the [schema](04-schema.md).
|
||||||
|
|
||||||
#### notify-batch
|
#### notify-batch
|
||||||
|
|
||||||
The `notify-batch` field allows you to specify an URL that will be called
|
The `notify-batch` field allows you to specify a URL that will be called
|
||||||
every time a user installs a package. The URL can be either an absolute path
|
every time a user installs a package. The URL can be either an absolute path
|
||||||
(that will use the same domain as the repository) or a fully qualified URL.
|
(that will use the same domain as the repository) or a fully qualified URL.
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ repository.
|
||||||
|
|
||||||
The `packages.json` file is loaded using a PHP stream. You can set extra options
|
The `packages.json` file is loaded using a PHP stream. You can set extra options
|
||||||
on that stream using the `options` parameter. You can set any valid PHP stream
|
on that stream using the `options` parameter. You can set any valid PHP stream
|
||||||
context option. See [Context options and parameters](http://php.net/manual/en/context.php)
|
context option. See [Context options and parameters](https://php.net/manual/en/context.php)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
### VCS
|
### VCS
|
||||||
|
@ -263,6 +263,10 @@ custom repository has priority over packagist. If you want to rename the
|
||||||
package, you should do so in the default (often master) branch and not in a
|
package, you should do so in the default (often master) branch and not in a
|
||||||
feature branch, since the package name is taken from the default branch.
|
feature branch, since the package name is taken from the default branch.
|
||||||
|
|
||||||
|
Also note that the override will not work if you change the `name` property
|
||||||
|
in your forked repository's composer.json file as this needs to match the
|
||||||
|
original for the override to work.
|
||||||
|
|
||||||
If other dependencies rely on the package you forked, it is possible to
|
If other dependencies rely on the package you forked, it is possible to
|
||||||
inline-alias it so that it matches a constraint that it otherwise would not.
|
inline-alias it so that it matches a constraint that it otherwise would not.
|
||||||
For more information [see the aliases article](articles/aliases.md).
|
For more information [see the aliases article](articles/aliases.md).
|
||||||
|
@ -293,8 +297,8 @@ The only requirement is the installation of SSH keys for a git client.
|
||||||
Git is not the only version control system supported by the VCS repository.
|
Git is not the only version control system supported by the VCS repository.
|
||||||
The following are supported:
|
The following are supported:
|
||||||
|
|
||||||
* **Git:** [git-scm.com](http://git-scm.com)
|
* **Git:** [git-scm.com](https://git-scm.com)
|
||||||
* **Subversion:** [subversion.apache.org](http://subversion.apache.org)
|
* **Subversion:** [subversion.apache.org](https://subversion.apache.org)
|
||||||
* **Mercurial:** [mercurial.selenic.com](http://mercurial.selenic.com)
|
* **Mercurial:** [mercurial.selenic.com](http://mercurial.selenic.com)
|
||||||
|
|
||||||
To get packages from these systems you need to have their respective clients
|
To get packages from these systems you need to have their respective clients
|
||||||
|
@ -312,7 +316,7 @@ should you need to specify one for whatever reason, you can use `git`, `svn` or
|
||||||
|
|
||||||
If you set the `no-api` key to `true` on a github repository it will clone the
|
If you set the `no-api` key to `true` on a github repository it will clone the
|
||||||
repository as it would with any other git repository instead of using the
|
repository as it would with any other git repository instead of using the
|
||||||
GitHub API. But unlike using the `git` driver directly, composer will still
|
GitHub API. But unlike using the `git` driver directly, Composer will still
|
||||||
attempt to use github's zip files.
|
attempt to use github's zip files.
|
||||||
|
|
||||||
#### Subversion Options
|
#### Subversion Options
|
||||||
|
@ -341,7 +345,7 @@ If you have no branches or tags directory you can disable them entirely by
|
||||||
setting the `branches-path` or `tags-path` to `false`.
|
setting the `branches-path` or `tags-path` to `false`.
|
||||||
|
|
||||||
If the package is in a sub-directory, e.g. `/trunk/foo/bar/composer.json` and
|
If the package is in a sub-directory, e.g. `/trunk/foo/bar/composer.json` and
|
||||||
`/tags/1.0/foo/bar/composer.json`, then you can make composer access it by
|
`/tags/1.0/foo/bar/composer.json`, then you can make Composer access it by
|
||||||
setting the `"package-path"` option to the sub-directory, in this example it
|
setting the `"package-path"` option to the sub-directory, in this example it
|
||||||
would be `"package-path": "foo/bar/"`.
|
would be `"package-path": "foo/bar/"`.
|
||||||
|
|
||||||
|
@ -389,7 +393,7 @@ Example using `pear2.php.net`:
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
"type": "pear",
|
"type": "pear",
|
||||||
"url": "http://pear2.php.net"
|
"url": "https://pear2.php.net"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -462,7 +466,7 @@ and `IntermediatePackage` from a Github repository:
|
||||||
|
|
||||||
### Package
|
### Package
|
||||||
|
|
||||||
If you want to use a project that does not support composer through any of the
|
If you want to use a project that does not support Composer through any of the
|
||||||
means above, you still can define the package yourself by using a `package`
|
means above, you still can define the package yourself by using a `package`
|
||||||
repository.
|
repository.
|
||||||
|
|
||||||
|
@ -517,7 +521,7 @@ Typically you would leave the source part off, as you don't really need it.
|
||||||
While you will probably want to put your packages on packagist most of the time,
|
While you will probably want to put your packages on packagist most of the time,
|
||||||
there are some use cases for hosting your own repository.
|
there are some use cases for hosting your own repository.
|
||||||
|
|
||||||
* **Private company packages:** If you are part of a company that uses composer
|
* **Private company packages:** If you are part of a company that uses Composer
|
||||||
for their packages internally, you might want to keep those packages private.
|
for their packages internally, you might want to keep those packages private.
|
||||||
|
|
||||||
* **Separate ecosystem:** If you have a project which has its own ecosystem,
|
* **Separate ecosystem:** If you have a project which has its own ecosystem,
|
||||||
|
@ -538,15 +542,16 @@ supported use case and changes will happen without caring for third parties
|
||||||
using the code.
|
using the code.
|
||||||
|
|
||||||
Packagist is a Symfony2 application, and it is [available on
|
Packagist is a Symfony2 application, and it is [available on
|
||||||
GitHub](https://github.com/composer/packagist). It uses composer internally and
|
GitHub](https://github.com/composer/packagist). It uses Composer internally and
|
||||||
acts as a proxy between VCS repositories and the composer users. It holds a list
|
acts as a proxy between VCS repositories and the Composer users. It holds a list
|
||||||
of all VCS packages, periodically re-crawls them, and exposes them as a composer
|
of all VCS packages, periodically re-crawls them, and exposes them as a Composer
|
||||||
repository.
|
repository.
|
||||||
|
|
||||||
### Toran Proxy
|
### Toran Proxy
|
||||||
|
|
||||||
[Toran Proxy](https://toranproxy.com/) is a web app much like Packagist but
|
[Toran Proxy](https://toranproxy.com/) is a web app much like Packagist but
|
||||||
providing private package hosting as well as mirroring/proxying of GitHub and packagist.org. Check its homepage and the [Satis/Toran Proxy article](articles/handling-private-packages-with-satis.md)
|
providing private package hosting as well as mirroring/proxying of GitHub and
|
||||||
|
packagist.org. Check its homepage and the [Satis/Toran Proxy article](articles/handling-private-packages-with-satis.md)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
### Satis
|
### Satis
|
||||||
|
@ -601,6 +606,42 @@ imported. When an archive with a newer version is added in the artifact folder
|
||||||
and you run `update`, that version will be imported as well and Composer will
|
and you run `update`, that version will be imported as well and Composer will
|
||||||
update to the latest version.
|
update to the latest version.
|
||||||
|
|
||||||
|
### Path
|
||||||
|
|
||||||
|
In addition to the artifact repository, you can use the path one, which allows
|
||||||
|
you to depend on a relative directory. This can be especially useful when dealing
|
||||||
|
with monolith repositories.
|
||||||
|
|
||||||
|
For instance, if you have the following directory structure in your repository:
|
||||||
|
```
|
||||||
|
- apps
|
||||||
|
\_ my-app
|
||||||
|
\_ composer.json
|
||||||
|
- packages
|
||||||
|
\_ my-package
|
||||||
|
\_ composer.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to add the package `my/package` as a dependency, in your `apps/my-app/composer.json`
|
||||||
|
file, you can use the following configuration:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../../packages/my-package"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"my/package": "*@dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Repository paths can also contain wildcards like ``*`` and ``?``.
|
||||||
|
> For details, see the [PHP glob function](http://php.net/glob).
|
||||||
|
|
||||||
## Disabling Packagist
|
## Disabling Packagist
|
||||||
|
|
||||||
You can disable the default Packagist repository by adding this to your
|
You can disable the default Packagist repository by adding this to your
|
||||||
|
@ -616,4 +657,4 @@ You can disable the default Packagist repository by adding this to your
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
← [Schema](04-schema.md) | [Community](06-community.md) →
|
← [Schema](04-schema.md) | [Config](06-config.md) →
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
# Config
|
||||||
|
|
||||||
|
This chapter will describe the `config` section of the `composer.json`
|
||||||
|
[schema](04-schema.md).
|
||||||
|
|
||||||
|
## process-timeout
|
||||||
|
|
||||||
|
Defaults to `300`. The duration processes like git clones can run before
|
||||||
|
Composer assumes they died out. You may need to make this higher if you have a
|
||||||
|
slow connection or huge vendors.
|
||||||
|
|
||||||
|
## use-include-path
|
||||||
|
|
||||||
|
Defaults to `false`. If `true`, the Composer autoloader will also look for classes
|
||||||
|
in the PHP include path.
|
||||||
|
|
||||||
|
## preferred-install
|
||||||
|
|
||||||
|
Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option
|
||||||
|
allows you to set the install method Composer will prefer to use.
|
||||||
|
|
||||||
|
## store-auths
|
||||||
|
|
||||||
|
What to do after prompting for authentication, one of: `true` (always store),
|
||||||
|
`false` (do not store) and `"prompt"` (ask every time), defaults to `"prompt"`.
|
||||||
|
|
||||||
|
## github-protocols
|
||||||
|
|
||||||
|
Defaults to `["git", "https", "ssh"]`. A list of protocols to use when cloning
|
||||||
|
from github.com, in priority order. You can reconfigure it to for example
|
||||||
|
prioritize the https protocol if you are behind a proxy or have somehow bad
|
||||||
|
performances with the git protocol.
|
||||||
|
|
||||||
|
## github-oauth
|
||||||
|
|
||||||
|
A list of domain names and oauth keys. For example using `{"github.com":
|
||||||
|
"oauthtoken"}` as the value of this option will use `oauthtoken` to access
|
||||||
|
private repositories on github and to circumvent the low IP-based rate limiting
|
||||||
|
of their API. [Read
|
||||||
|
more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) on how to get
|
||||||
|
an OAuth token for GitHub.
|
||||||
|
|
||||||
|
## http-basic
|
||||||
|
|
||||||
|
A list of domain names and username/passwords to authenticate against them. For
|
||||||
|
example using `{"example.org": {"username": "alice", "password": "foo"}` as the
|
||||||
|
value of this option will let Composer authenticate against example.org.
|
||||||
|
|
||||||
|
> **Note:** Authentication-related config options like `http-basic` and
|
||||||
|
> `github-oauth` can also be specified inside a `auth.json` file that goes
|
||||||
|
> besides your `composer.json`. That way you can gitignore it and every
|
||||||
|
> developer can place their own credentials in there.
|
||||||
|
|
||||||
|
## platform
|
||||||
|
|
||||||
|
Lets you fake platform packages (PHP and extensions) so that you can emulate a
|
||||||
|
production env or define your target platform in the config. Example: `{"php":
|
||||||
|
"5.4", "ext-something": "4.0"}`.
|
||||||
|
|
||||||
|
## vendor-dir
|
||||||
|
|
||||||
|
Defaults to `vendor`. You can install dependencies into a different directory if
|
||||||
|
you want to. `$HOME` and `~` will be replaced by your home directory's path in
|
||||||
|
vendor-dir and all `*-dir` options below.
|
||||||
|
|
||||||
|
## bin-dir
|
||||||
|
|
||||||
|
Defaults to `vendor/bin`. If a project includes binaries, they will be symlinked
|
||||||
|
into this directory.
|
||||||
|
|
||||||
|
## cache-dir
|
||||||
|
|
||||||
|
Defaults to `$COMPOSER_HOME/cache` on unix systems and
|
||||||
|
`C:\Users\<user>\AppData\Local\Composer` on Windows. Stores all the caches used
|
||||||
|
by Composer. See also [COMPOSER_HOME](03-cli.md#composer-home).
|
||||||
|
|
||||||
|
## cache-files-dir
|
||||||
|
|
||||||
|
Defaults to `$cache-dir/files`. Stores the zip archives of packages.
|
||||||
|
|
||||||
|
## cache-repo-dir
|
||||||
|
|
||||||
|
Defaults to `$cache-dir/repo`. Stores repository metadata for the `composer`
|
||||||
|
type and the VCS repos of type `svn`, `github` and `bitbucket`.
|
||||||
|
|
||||||
|
## cache-vcs-dir
|
||||||
|
|
||||||
|
Defaults to `$cache-dir/vcs`. Stores VCS clones for loading VCS repository
|
||||||
|
metadata for the `git`/`hg` types and to speed up installs.
|
||||||
|
|
||||||
|
## cache-files-ttl
|
||||||
|
|
||||||
|
Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ..)
|
||||||
|
packages that it downloads. Those are purged after six months of being unused by
|
||||||
|
default. This option allows you to tweak this duration (in seconds) or disable
|
||||||
|
it completely by setting it to 0.
|
||||||
|
|
||||||
|
## cache-files-maxsize
|
||||||
|
|
||||||
|
Defaults to `300MiB`. Composer caches all dist (zip, tar, ..) packages that it
|
||||||
|
downloads. When the garbage collection is periodically ran, this is the maximum
|
||||||
|
size the cache will be able to use. Older (less used) files will be removed
|
||||||
|
first until the cache fits.
|
||||||
|
|
||||||
|
## bin-compat
|
||||||
|
|
||||||
|
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
|
||||||
|
If it is `auto` then Composer only installs .bat proxy files when on Windows. If
|
||||||
|
set to `full` then both .bat files for Windows and scripts for Unix-based
|
||||||
|
operating systems will be installed for each binary. This is mainly useful if you
|
||||||
|
run Composer inside a linux VM but still want the .bat proxies available for use
|
||||||
|
in the Windows host OS.
|
||||||
|
|
||||||
|
## prepend-autoloader
|
||||||
|
|
||||||
|
Defaults to `true`. If `false`, the Composer autoloader will not be prepended to
|
||||||
|
existing autoloaders. This is sometimes required to fix interoperability issues
|
||||||
|
with other autoloaders.
|
||||||
|
|
||||||
|
## autoloader-suffix
|
||||||
|
|
||||||
|
Defaults to `null`. String to be used as a suffix for the generated Composer
|
||||||
|
autoloader. When null a random one will be generated.
|
||||||
|
|
||||||
|
## optimize-autoloader
|
||||||
|
|
||||||
|
Defaults to `false`. If `true`, always optimize when dumping the autoloader.
|
||||||
|
|
||||||
|
## classmap-authoritative
|
||||||
|
|
||||||
|
Defaults to `false`. If `true`, the Composer autoloader will only load classes
|
||||||
|
from the classmap. Implies `optimize-autoloader`.
|
||||||
|
|
||||||
|
## github-domains
|
||||||
|
|
||||||
|
Defaults to `["github.com"]`. A list of domains to use in github mode. This is
|
||||||
|
used for GitHub Enterprise setups.
|
||||||
|
|
||||||
|
## github-expose-hostname
|
||||||
|
|
||||||
|
Defaults to `true`. If `false`, the OAuth tokens created to access the
|
||||||
|
github API will have a date instead of the machine hostname.
|
||||||
|
|
||||||
|
## gitlab-domains
|
||||||
|
|
||||||
|
Defaults to `["gitlab.com"]`. A list of domains of GitLab servers.
|
||||||
|
This is used if you use the `gitlab` repository type.
|
||||||
|
|
||||||
|
## notify-on-install
|
||||||
|
|
||||||
|
Defaults to `true`. Composer allows repositories to define a notification URL,
|
||||||
|
so that they get notified whenever a package from that repository is installed.
|
||||||
|
This option allows you to disable that behaviour.
|
||||||
|
|
||||||
|
## discard-changes
|
||||||
|
|
||||||
|
Defaults to `false` and can be any of `true`, `false` or `"stash"`. This option
|
||||||
|
allows you to set the default style of handling dirty updates when in
|
||||||
|
non-interactive mode. `true` will always discard changes in vendors, while
|
||||||
|
`"stash"` will try to stash and reapply. Use this for CI servers or deploy
|
||||||
|
scripts if you tend to have modified vendors.
|
||||||
|
|
||||||
|
## archive-format
|
||||||
|
|
||||||
|
Defaults to `tar`. Composer allows you to add a default archive format when the
|
||||||
|
workflow needs to create a dedicated archiving format.
|
||||||
|
|
||||||
|
## archive-dir
|
||||||
|
|
||||||
|
Defaults to `.`. Composer allows you to add a default archive directory when the
|
||||||
|
workflow needs to create a dedicated archiving format. Or for easier development
|
||||||
|
between modules.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"archive-dir": "/home/user/.composer/repo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
← [Repositories](05-repositories.md) | [Community](07-community.md) →
|
|
@ -1,12 +1,14 @@
|
||||||
# Community
|
# Community
|
||||||
|
|
||||||
There are many people using composer already, and quite a few of them are
|
There are many people using Composer already, and quite a few of them are
|
||||||
contributing.
|
contributing.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you would like to contribute to composer, please read the
|
If you would like to contribute to Composer, please read the
|
||||||
[README](https://github.com/composer/composer).
|
[README](https://github.com/composer/composer) and
|
||||||
|
[CONTRIBUTING](https://github.com//composer/composer/blob/master/CONTRIBUTING.md)
|
||||||
|
documents.
|
||||||
|
|
||||||
The most important guidelines are described as follows:
|
The most important guidelines are described as follows:
|
||||||
|
|
||||||
|
@ -17,18 +19,18 @@ The most important guidelines are described as follows:
|
||||||
> Fork the project, create a feature branch, and send us a pull request.
|
> Fork the project, create a feature branch, and send us a pull request.
|
||||||
>
|
>
|
||||||
> To ensure a consistent code base, you should make sure the code follows
|
> To ensure a consistent code base, you should make sure the code follows
|
||||||
> the [Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html)
|
> the [Coding Standards](https://symfony.com/doc/2.0/contributing/code/standards.html)
|
||||||
> which we borrowed from Symfony.
|
> which we borrowed from Symfony.
|
||||||
|
|
||||||
## IRC / mailing list
|
## IRC / mailing list
|
||||||
|
|
||||||
Mailing lists for [user support](http://groups.google.com/group/composer-users) and
|
Mailing lists for [user support](https://groups.google.com/group/composer-users) and
|
||||||
[development](http://groups.google.com/group/composer-dev).
|
[development](https://groups.google.com/group/composer-dev).
|
||||||
|
|
||||||
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
|
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
|
||||||
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
|
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
|
||||||
|
|
||||||
Stack Overflow has a growing collection of
|
Stack Overflow has a growing collection of
|
||||||
[Composer related questions](http://stackoverflow.com/questions/tagged/composer-php).
|
[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
|
||||||
|
|
||||||
← [Repositories](05-repositories.md)
|
← [Config](06-config.md)
|
|
@ -38,8 +38,8 @@ specifying a `branch-alias` field under `extra` in `composer.json`:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you alias a non-comparible version (such as dev-develop) `dev-` must prefix the
|
If you alias a non-comparable version (such as dev-develop) `dev-` must prefix the
|
||||||
branch name. You may also alias a comparible version (i.e. start with numbers,
|
branch name. You may also alias a comparable version (i.e. start with numbers,
|
||||||
and end with `.x-dev`), but only as a more specific version.
|
and end with `.x-dev`), but only as a more specific version.
|
||||||
For example, 1.x-dev could be aliased as 1.2.x-dev.
|
For example, 1.x-dev could be aliased as 1.2.x-dev.
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ An example composer.json of such a template package would be:
|
||||||
## Creating an Installer
|
## Creating an Installer
|
||||||
|
|
||||||
A Custom Installer is defined as a class that implements the
|
A Custom Installer is defined as a class that implements the
|
||||||
[`Composer\Installer\InstallerInterface`][3] and is usually distributed in a
|
[`Composer\Installer\InstallerInterface`][4] and is usually distributed in a
|
||||||
Composer Plugin.
|
Composer Plugin.
|
||||||
|
|
||||||
A basic Installer Plugin would thus compose of three files:
|
A basic Installer Plugin would thus compose of three files:
|
||||||
|
|
|
@ -21,7 +21,7 @@ find more information about how to set it up and use it on the [Toran Proxy](htt
|
||||||
Satis on the other hand is open source but only a static `composer`
|
Satis on the other hand is open source but only a static `composer`
|
||||||
repository generator. It is a bit like an ultra-lightweight, static file-based
|
repository generator. It is a bit like an ultra-lightweight, static file-based
|
||||||
version of packagist and can be used to host the metadata of your company's
|
version of packagist and can be used to host the metadata of your company's
|
||||||
private packages, or your own. You can get it from [GitHub](http://github.com/composer/satis)
|
private packages, or your own. You can get it from [GitHub](https://github.com/composer/satis)
|
||||||
or install via CLI:
|
or install via CLI:
|
||||||
`php composer.phar create-project composer/satis --stability=dev --keep-vcs`.
|
`php composer.phar create-project composer/satis --stability=dev --keep-vcs`.
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ The default file Satis looks for is `satis.json` in the root of the repository.
|
||||||
"name": "My Repository",
|
"name": "My Repository",
|
||||||
"homepage": "http://packages.example.org",
|
"homepage": "http://packages.example.org",
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo" },
|
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
|
||||||
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
||||||
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" }
|
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
|
||||||
],
|
],
|
||||||
"require-all": true
|
"require-all": true
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ constraint if you want really specific versions.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo" },
|
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
|
||||||
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
||||||
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" }
|
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"company/package": "*",
|
"company/package": "*",
|
||||||
|
@ -73,7 +73,7 @@ constraint if you want really specific versions.
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you've done this, you just run `php bin/satis build <configuration file> <build dir>`.
|
Once you've done this, you just run `php bin/satis build <configuration file> <build dir>`.
|
||||||
For example `php bin/satis build config.json web/` would read the `config.json`
|
For example `php bin/satis build satis.json web/` would read the `satis.json`
|
||||||
file and build a static repository inside the `web/` directory.
|
file and build a static repository inside the `web/` directory.
|
||||||
|
|
||||||
When you ironed out that process, what you would typically do is run this
|
When you ironed out that process, what you would typically do is run this
|
||||||
|
@ -135,7 +135,7 @@ Example using a custom repository using SSH (requires the SSH2 PECL extension):
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Tip:** See [ssh2 context options](http://www.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options) for more information.
|
> **Tip:** See [ssh2 context options](https://www.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options) for more information.
|
||||||
|
|
||||||
Example using HTTP over SSL using a client certificate:
|
Example using HTTP over SSL using a client certificate:
|
||||||
|
|
||||||
|
@ -155,7 +155,45 @@ Example using HTTP over SSL using a client certificate:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Tip:** See [ssl context options](http://www.php.net/manual/en/context.ssl.php) for more information.
|
> **Tip:** See [ssl context options](https://www.php.net/manual/en/context.ssl.php) for more information.
|
||||||
|
|
||||||
|
Example using a custom HTTP Header field for token authentication:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "https://example.org",
|
||||||
|
"options": {
|
||||||
|
"http": {
|
||||||
|
"header": [
|
||||||
|
"API-TOKEN: YOUR-API-TOKEN"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
When your private repositories are password protected, you can store the authentication details permanently.
|
||||||
|
The first time Composer needs to authenticate against some domain it will prompt you for a username/password
|
||||||
|
and then you will be asked whether you want to store it.
|
||||||
|
|
||||||
|
The storage can be done either globally in the `COMPOSER_HOME/auth.json` file (`COMPOSER_HOME` defaults to
|
||||||
|
`~/.composer` or `%APPDATA%/Composer` on Windows) or also in the project directory directly sitting besides your
|
||||||
|
composer.json.
|
||||||
|
|
||||||
|
You can also configure these by hand using the config command if you need to configure a production machine
|
||||||
|
to be able to run non-interactive installs. For example to enter credentials for example.org one could type:
|
||||||
|
|
||||||
|
composer config http-basic.example.org username password
|
||||||
|
|
||||||
|
That will store it in the current directory's auth.json, but if you want it available globally you can use the
|
||||||
|
`--global` (`-g`) flag.
|
||||||
|
|
||||||
### Downloads
|
### Downloads
|
||||||
|
|
||||||
|
@ -194,8 +232,8 @@ Once enabled, all downloads (include those from GitHub and BitBucket) will be re
|
||||||
Prefixing the URL with another host is especially helpful if the downloads end up in a private Amazon S3
|
Prefixing the URL with another host is especially helpful if the downloads end up in a private Amazon S3
|
||||||
bucket or on a CDN host. A CDN would drastically improve download times and therefore package installation.
|
bucket or on a CDN host. A CDN would drastically improve download times and therefore package installation.
|
||||||
|
|
||||||
Example: A `prefix-url` of `http://my-bucket.s3.amazonaws.com` (and `directory` set to `dist`) creates download URLs
|
Example: A `prefix-url` of `https://my-bucket.s3.amazonaws.com` (and `directory` set to `dist`) creates download URLs
|
||||||
which look like the following: `http://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`.
|
which look like the following: `https://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`.
|
||||||
|
|
||||||
|
|
||||||
### Resolving dependencies
|
### Resolving dependencies
|
||||||
|
|
|
@ -17,7 +17,7 @@ of credentials inline with the repository specification such as:
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
"type": "composer",
|
"type": "composer",
|
||||||
"url": "http://extremely:secret@repo.example.org"
|
"url": "https://extremely:secret@repo.example.org"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ username/password pairs, for example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"basic-auth": {
|
"http-basic": {
|
||||||
"repo.example1.org": {
|
"repo.example1.org": {
|
||||||
"username": "my-username1",
|
"username": "my-username1",
|
||||||
"password": "my-secret-password1"
|
"password": "my-secret-password1"
|
||||||
|
@ -55,5 +55,5 @@ username/password pairs, for example:
|
||||||
|
|
||||||
The main advantage of the auth.json file is that it can be gitignored so
|
The main advantage of the auth.json file is that it can be gitignored so
|
||||||
that every developer in your team can place their own credentials in there,
|
that every developer in your team can place their own credentials in there,
|
||||||
which makes revokation of credentials much easier than if you all share the
|
which makes revocation of credentials much easier than if you all share the
|
||||||
same.
|
same.
|
||||||
|
|
|
@ -16,7 +16,7 @@ specific logic.
|
||||||
|
|
||||||
## Creating a Plugin
|
## Creating a Plugin
|
||||||
|
|
||||||
A plugin is a regular composer package which ships its code as part of the
|
A plugin is a regular Composer package which ships its code as part of the
|
||||||
package and may also depend on further packages.
|
package and may also depend on further packages.
|
||||||
|
|
||||||
### Plugin Package
|
### Plugin Package
|
||||||
|
@ -24,23 +24,30 @@ package and may also depend on further packages.
|
||||||
The package file is the same as any other package file but with the following
|
The package file is the same as any other package file but with the following
|
||||||
requirements:
|
requirements:
|
||||||
|
|
||||||
1. the [type][1] attribute must be `composer-plugin`.
|
1. The [type][1] attribute must be `composer-plugin`.
|
||||||
2. the [extra][2] attribute must contain an element `class` defining the
|
2. The [extra][2] attribute must contain an element `class` defining the
|
||||||
class name of the plugin (including namespace). If a package contains
|
class name of the plugin (including namespace). If a package contains
|
||||||
multiple plugins this can be array of class names.
|
multiple plugins, this can be array of class names.
|
||||||
|
3. You must require the special package called `composer-plugin-api`
|
||||||
|
to define which Plugin API versions your plugin is compatible with.
|
||||||
|
|
||||||
Additionally you must require the special package called `composer-plugin-api`
|
The required version of the `composer-plugin-api` follows the same [rules][7]
|
||||||
to define which composer API versions your plugin is compatible with. The
|
as a normal package's.
|
||||||
current composer plugin API version is 1.0.0.
|
|
||||||
|
|
||||||
For example
|
The current composer plugin API version is 1.0.0.
|
||||||
|
|
||||||
|
An example of a valid plugin `composer.json` file (with the autoloading
|
||||||
|
part omitted):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "my/plugin-package",
|
"name": "my/plugin-package",
|
||||||
"type": "composer-plugin",
|
"type": "composer-plugin",
|
||||||
"require": {
|
"require": {
|
||||||
"composer-plugin-api": "1.0.0"
|
"composer-plugin-api": "^1.0"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"class": "My\\Plugin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -82,15 +89,7 @@ Furthermore plugins may implement the
|
||||||
event handlers automatically registered with the `EventDispatcher` when the
|
event handlers automatically registered with the `EventDispatcher` when the
|
||||||
plugin is loaded.
|
plugin is loaded.
|
||||||
|
|
||||||
The events available for plugins are:
|
Plugin can subscribe to any of the available [script events](scripts.md#event-names).
|
||||||
|
|
||||||
* **COMMAND**, is called at the beginning of all commands that load plugins.
|
|
||||||
It provides you with access to the input and output objects of the program.
|
|
||||||
* **PRE_FILE_DOWNLOAD**, is triggered before files are downloaded and allows
|
|
||||||
you to manipulate the `RemoteFilesystem` object prior to downloading files
|
|
||||||
based on the URL to be downloaded.
|
|
||||||
|
|
||||||
> A plugin can also subscribe to [script events][7].
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -148,7 +147,7 @@ list of installed packages. Additionally all plugin packages installed in the
|
||||||
local project plugins are loaded.
|
local project plugins are loaded.
|
||||||
|
|
||||||
> You may pass the `--no-plugins` option to composer commands to disable all
|
> You may pass the `--no-plugins` option to composer commands to disable all
|
||||||
> installed commands. This may be particularly helpful if any of the plugins
|
> installed plugins. This may be particularly helpful if any of the plugins
|
||||||
> causes errors and you wish to update or uninstall it.
|
> causes errors and you wish to update or uninstall it.
|
||||||
|
|
||||||
[1]: ../04-schema.md#type
|
[1]: ../04-schema.md#type
|
||||||
|
@ -157,4 +156,4 @@ local project plugins are loaded.
|
||||||
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
|
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
|
||||||
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
|
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
|
||||||
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
|
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
|
||||||
[7]: ./scripts.md#event-names
|
[7]: ../01-basic-usage.md#package-versions
|
||||||
|
|
|
@ -20,30 +20,46 @@ the Composer execution process.
|
||||||
|
|
||||||
Composer fires the following named events during its execution process:
|
Composer fires the following named events during its execution process:
|
||||||
|
|
||||||
|
### Command Events
|
||||||
|
|
||||||
- **pre-install-cmd**: occurs before the `install` command is executed.
|
- **pre-install-cmd**: occurs before the `install` command is executed.
|
||||||
- **post-install-cmd**: occurs after the `install` command is executed.
|
- **post-install-cmd**: occurs after the `install` command has been executed.
|
||||||
- **pre-update-cmd**: occurs before the `update` command is executed.
|
- **pre-update-cmd**: occurs before the `update` command is executed.
|
||||||
- **post-update-cmd**: occurs after the `update` command is executed.
|
- **post-update-cmd**: occurs after the `update` command has been executed.
|
||||||
- **pre-status-cmd**: occurs before the `status` command is executed.
|
- **pre-status-cmd**: occurs before the `status` command is executed.
|
||||||
- **post-status-cmd**: occurs after the `status` command is executed.
|
- **post-status-cmd**: occurs after the `status` command has been executed.
|
||||||
- **pre-dependencies-solving**: occurs before the dependencies are resolved.
|
- **pre-archive-cmd**: occurs before the `archive` command is executed.
|
||||||
- **post-dependencies-solving**: occurs after the dependencies are resolved.
|
- **post-archive-cmd**: occurs after the `archive` command has been executed.
|
||||||
- **pre-package-install**: occurs before a package is installed.
|
|
||||||
- **post-package-install**: occurs after a package is installed.
|
|
||||||
- **pre-package-update**: occurs before a package is updated.
|
|
||||||
- **post-package-update**: occurs after a package is updated.
|
|
||||||
- **pre-package-uninstall**: occurs before a package has been uninstalled.
|
|
||||||
- **post-package-uninstall**: occurs after a package has been uninstalled.
|
|
||||||
- **pre-autoload-dump**: occurs before the autoloader is dumped, either
|
- **pre-autoload-dump**: occurs before the autoloader is dumped, either
|
||||||
during `install`/`update`, or via the `dump-autoload` command.
|
during `install`/`update`, or via the `dump-autoload` command.
|
||||||
- **post-autoload-dump**: occurs after the autoloader is dumped, either
|
- **post-autoload-dump**: occurs after the autoloader has been dumped, either
|
||||||
during `install`/`update`, or via the `dump-autoload` command.
|
during `install`/`update`, or via the `dump-autoload` command.
|
||||||
- **post-root-package-install**: occurs after the root package has been
|
- **post-root-package-install**: occurs after the root package has been
|
||||||
installed, during the `create-project` command.
|
installed, during the `create-project` command.
|
||||||
- **post-create-project-cmd**: occurs after the `create-project` command is
|
- **post-create-project-cmd**: occurs after the `create-project` command has
|
||||||
executed.
|
been executed.
|
||||||
- **pre-archive-cmd**: occurs before the `archive` command is executed.
|
|
||||||
- **post-archive-cmd**: occurs after the `archive` command is executed.
|
### Installer Events
|
||||||
|
|
||||||
|
- **pre-dependencies-solving**: occurs before the dependencies are resolved.
|
||||||
|
- **post-dependencies-solving**: occurs after the dependencies have been resolved.
|
||||||
|
|
||||||
|
### Package Events
|
||||||
|
|
||||||
|
- **pre-package-install**: occurs before a package is installed.
|
||||||
|
- **post-package-install**: occurs after a package has been installed.
|
||||||
|
- **pre-package-update**: occurs before a package is updated.
|
||||||
|
- **post-package-update**: occurs after a package has been updated.
|
||||||
|
- **pre-package-uninstall**: occurs before a package is uninstalled.
|
||||||
|
- **post-package-uninstall**: occurs after a package has been uninstalled.
|
||||||
|
|
||||||
|
### Plugin Events
|
||||||
|
|
||||||
|
- **command**: occurs before any Composer Command is executed on the CLI. It
|
||||||
|
provides you with access to the input and output objects of the program.
|
||||||
|
- **pre-file-download**: occurs before files are downloaded and allows
|
||||||
|
you to manipulate the `RemoteFilesystem` object prior to downloading files
|
||||||
|
based on the URL to be downloaded.
|
||||||
|
|
||||||
> **Note:** Composer makes no assumptions about the state of your dependencies
|
> **Note:** Composer makes no assumptions about the state of your dependencies
|
||||||
> prior to `install` or `update`. Therefore, you should not specify scripts
|
> prior to `install` or `update`. Therefore, you should not specify scripts
|
||||||
|
@ -66,6 +82,10 @@ For any given event:
|
||||||
and command-line executable commands.
|
and command-line executable commands.
|
||||||
- PHP classes containing defined callbacks must be autoloadable via Composer's
|
- PHP classes containing defined callbacks must be autoloadable via Composer's
|
||||||
autoload functionality.
|
autoload functionality.
|
||||||
|
- Callbacks can only autoload classes from psr-0, psr-4 and classmap
|
||||||
|
definitions. If a defined callback relies on functions defined outside of a
|
||||||
|
class, the callback itself is responsible for loading the file containing these
|
||||||
|
functions.
|
||||||
|
|
||||||
Script definition example:
|
Script definition example:
|
||||||
|
|
||||||
|
@ -80,7 +100,10 @@ Script definition example:
|
||||||
"MyVendor\\MyClass::warmCache",
|
"MyVendor\\MyClass::warmCache",
|
||||||
"phpunit -c app/"
|
"phpunit -c app/"
|
||||||
],
|
],
|
||||||
"post-create-project-cmd" : [
|
"post-autoload-dump": [
|
||||||
|
"MyVendor\\MyClass::postAutoloadDump"
|
||||||
|
],
|
||||||
|
"post-create-project-cmd": [
|
||||||
"php -r \"copy('config/local-example.php', 'config/local.php');\""
|
"php -r \"copy('config/local-example.php', 'config/local.php');\""
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -96,6 +119,7 @@ that might be used to execute the PHP callbacks:
|
||||||
namespace MyVendor;
|
namespace MyVendor;
|
||||||
|
|
||||||
use Composer\Script\Event;
|
use Composer\Script\Event;
|
||||||
|
use Composer\Installer\PackageEvent;
|
||||||
|
|
||||||
class MyClass
|
class MyClass
|
||||||
{
|
{
|
||||||
|
@ -105,7 +129,15 @@ class MyClass
|
||||||
// do stuff
|
// do stuff
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function postPackageInstall(Event $event)
|
public static function postAutoloadDump(Event $event)
|
||||||
|
{
|
||||||
|
$vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
|
||||||
|
require $vendorDir . '/autoload.php';
|
||||||
|
|
||||||
|
some_function_from_an_autoloaded_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function postPackageInstall(PackageEvent $event)
|
||||||
{
|
{
|
||||||
$installedPackage = $event->getOperation()->getPackage();
|
$installedPackage = $event->getOperation()->getPackage();
|
||||||
// do stuff
|
// do stuff
|
||||||
|
@ -118,14 +150,21 @@ class MyClass
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When an event is fired, Composer's internal event handler receives a
|
When an event is fired, your PHP callback receives as first argument an
|
||||||
`Composer\Script\Event` object, which is passed as the first argument to your
|
`Composer\EventDispatcher\Event` object. This object has a `getName()` method
|
||||||
PHP callback. This `Event` object has getters for other contextual objects:
|
that lets you retrieve event name.
|
||||||
|
|
||||||
- `getComposer()`: returns the current instance of `Composer\Composer`
|
Depending on the script types (see list above) you will get various event
|
||||||
- `getName()`: returns the name of the event being fired as a string
|
subclasses containing various getters with relevant data and associated
|
||||||
- `getIO()`: returns the current input/output stream which implements
|
objects:
|
||||||
`Composer\IO\IOInterface` for writing to the console
|
|
||||||
|
- Base class: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html)
|
||||||
|
- Command Events: [`Composer\Script\Event`](https://getcomposer.org/apidoc/master/Composer/Script/Event.html)
|
||||||
|
- Installer Events: [`Composer\Installer\InstallerEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/InstallerEvent.html)
|
||||||
|
- Package Events: [`Composer\Installer\PackageEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/PackageEvent.html)
|
||||||
|
- Plugin Events:
|
||||||
|
- command: [`Composer\Plugin\CommandEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/CommandEvent.html)
|
||||||
|
- pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/PreFileDownloadEvent.html)
|
||||||
|
|
||||||
## Running scripts manually
|
## Running scripts manually
|
||||||
|
|
||||||
|
|
|
@ -43,12 +43,16 @@ This is a list of common pitfalls on using Composer, and how to avoid them.
|
||||||
5. If you are updating to a recently published version of a package, be aware that
|
5. If you are updating to a recently published version of a package, be aware that
|
||||||
Packagist has a delay of up to 1 minute before new packages are visible to Composer.
|
Packagist has a delay of up to 1 minute before new packages are visible to Composer.
|
||||||
|
|
||||||
|
6. If you are updating a single package, it may depend on newer versions itself.
|
||||||
|
In this case add the `--with-dependencies` argument **or** add all dependencies which
|
||||||
|
need an update to the command.
|
||||||
|
|
||||||
## Package not found on travis-ci.org
|
## Package not found on travis-ci.org
|
||||||
|
|
||||||
1. Check the ["Package not found"](#package-not-found) item above.
|
1. Check the ["Package not found"](#package-not-found) item above.
|
||||||
|
|
||||||
2. If the package tested is a dependency of one of its dependencies (cyclic
|
2. If the package tested is a dependency of one of its dependencies (cyclic
|
||||||
dependency), the problem might be that composer is not able to detect the version
|
dependency), the problem might be that Composer is not able to detect the version
|
||||||
of the package properly. If it is a git clone it is generally alright and Composer
|
of the package properly. If it is a git clone it is generally alright and Composer
|
||||||
will detect the version of the current branch, but travis does shallow clones so
|
will detect the version of the current branch, but travis does shallow clones so
|
||||||
that process can fail when testing pull requests and feature branches in general.
|
that process can fail when testing pull requests and feature branches in general.
|
||||||
|
@ -58,11 +62,22 @@ This is a list of common pitfalls on using Composer, and how to avoid them.
|
||||||
Use: `before_script: COMPOSER_ROOT_VERSION=dev-master composer install` to export
|
Use: `before_script: COMPOSER_ROOT_VERSION=dev-master composer install` to export
|
||||||
the variable for the call to composer.
|
the variable for the call to composer.
|
||||||
|
|
||||||
|
## Package not found in a Jenkins-build
|
||||||
|
|
||||||
|
1. Check the ["Package not found"](#package-not-found) item above.
|
||||||
|
2. Reason for failing is similar to the problem which can occur on travis-ci.org: The
|
||||||
|
git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As
|
||||||
|
a result, Composer is not able to identify the version of the current checked out branch
|
||||||
|
and may not be able to resolve a cyclic dependency. To solve this problem, you can use
|
||||||
|
the "Additional Behaviours" -> "Check out to specific local branch" in your Git-settings
|
||||||
|
for your Jenkins-job, where your "local branch" shall be the same branch as you are
|
||||||
|
checking out. Using this, the checkout will not be in detached state any more and cyclic
|
||||||
|
dependency is recognized correctly.
|
||||||
|
|
||||||
## Need to override a package version
|
## Need to override a package version
|
||||||
|
|
||||||
Let say your project depends on package A which in turn depends on a specific
|
Let's say your project depends on package A, which in turn depends on a specific
|
||||||
version of package B (say 0.1) and you need a different version of that
|
version of package B (say 0.1). But you need a different version of said package B (say 0.11).
|
||||||
package - version 0.11.
|
|
||||||
|
|
||||||
You can fix this by aliasing version 0.11 to 0.1:
|
You can fix this by aliasing version 0.11 to 0.1:
|
||||||
|
|
||||||
|
@ -87,7 +102,7 @@ If composer shows memory errors on some commands:
|
||||||
|
|
||||||
The PHP `memory_limit` should be increased.
|
The PHP `memory_limit` should be increased.
|
||||||
|
|
||||||
> **Note:** Composer internally increases the `memory_limit` to `512M`.
|
> **Note:** Composer internally increases the `memory_limit` to `1G`.
|
||||||
> If you have memory issues when using composer, please consider [creating
|
> If you have memory issues when using composer, please consider [creating
|
||||||
> an issue ticket](https://github.com/composer/composer/issues) so we can look into it.
|
> an issue ticket](https://github.com/composer/composer/issues) so we can look into it.
|
||||||
|
|
||||||
|
@ -101,7 +116,7 @@ Try increasing the limit in your `php.ini` file (ex. `/etc/php5/cli/php.ini` for
|
||||||
Debian-like systems):
|
Debian-like systems):
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
; Use -1 for unlimited or define an explicit value like 512M
|
; Use -1 for unlimited or define an explicit value like 2G
|
||||||
memory_limit = -1
|
memory_limit = -1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -157,3 +172,28 @@ To enable the swap you can use for example:
|
||||||
/sbin/mkswap /var/swap.1
|
/sbin/mkswap /var/swap.1
|
||||||
/sbin/swapon /var/swap.1
|
/sbin/swapon /var/swap.1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Degraded Mode
|
||||||
|
|
||||||
|
Due to some intermittent issues on Travis and other systems, we introduced a
|
||||||
|
degraded network mode which helps Composer finish successfully but disables
|
||||||
|
a few optimizations. This is enabled automatically when an issue is first
|
||||||
|
detected. If you see this issue sporadically you probably don't have to worry
|
||||||
|
(a slow or overloaded network can also cause those time outs), but if it
|
||||||
|
appears repeatedly you might want to look at the options below to identify
|
||||||
|
and resolve it.
|
||||||
|
|
||||||
|
If you have been pointed to this page, you want to check a few things:
|
||||||
|
|
||||||
|
- If you are using ESET antivirus, go in "Advanced Settings" and disable "HTTP-scanner"
|
||||||
|
under "web access protection"
|
||||||
|
- If you are using IPv6, try disabling it. If that solves your issues, get in touch
|
||||||
|
with your ISP or server host, the problem is not at the Packagist level but in the
|
||||||
|
routing rules between you and Packagist (i.e. the internet at large). The best way to get
|
||||||
|
these fixed is raise awareness to the network engineers that have the power to fix it.
|
||||||
|
|
||||||
|
To disable IPv6 on Linux, try using this command which appends a
|
||||||
|
rule preferring IPv4 over IPv6 to your config:
|
||||||
|
|
||||||
|
`sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"`
|
||||||
|
- If none of the above helped, please report the error.
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
<!--
|
||||||
|
tagline: Version constraints explained.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Versions
|
||||||
|
|
||||||
|
## Basic Constraints
|
||||||
|
|
||||||
|
### Exact
|
||||||
|
|
||||||
|
You can specify the exact version of a package. This will tell Composer to
|
||||||
|
install this version and this version only. If other dependencies require
|
||||||
|
a different version, the solver will ultimately fail and abort any install
|
||||||
|
or update procedures.
|
||||||
|
|
||||||
|
Example: `1.0.2`
|
||||||
|
|
||||||
|
### Range
|
||||||
|
|
||||||
|
By using comparison operators you can specify ranges of valid versions. Valid
|
||||||
|
operators are `>`, `>=`, `<`, `<=`, `!=`.
|
||||||
|
|
||||||
|
You can define multiple ranges. Ranges separated by a space (<code> </code>)
|
||||||
|
or comma (`,`) will be treated as a **logical AND**. A double pipe (`||`)
|
||||||
|
will be treated as a **logical OR**. AND has higher precedence than OR.
|
||||||
|
|
||||||
|
> **Note:** Be careful when using unbounded ranges as you might end up
|
||||||
|
> unexpectedly installing versions that break backwards compatibility.
|
||||||
|
> Consider using the [caret](#caret) operator instead for safety.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
* `>=1.0`
|
||||||
|
* `>=1.0 <2.0`
|
||||||
|
* `>=1.0 <1.1 || >=1.2`
|
||||||
|
|
||||||
|
### Range (Hyphen)
|
||||||
|
|
||||||
|
Inclusive set of versions. Partial versions on the right include are completed
|
||||||
|
with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the
|
||||||
|
`2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to
|
||||||
|
`>=1.0.0 <=2.1.0`.
|
||||||
|
|
||||||
|
Example: `1.0 - 2.0`
|
||||||
|
|
||||||
|
### Wildcard
|
||||||
|
|
||||||
|
You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of
|
||||||
|
`>=1.0 <1.1`.
|
||||||
|
|
||||||
|
Example: `1.0.*`
|
||||||
|
|
||||||
|
## Next Significant Release Operators
|
||||||
|
|
||||||
|
### Tilde
|
||||||
|
|
||||||
|
The `~` operator is best explained by example: `~1.2` is equivalent to
|
||||||
|
`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
|
||||||
|
it is mostly useful for projects respecting [semantic
|
||||||
|
versioning](http://semver.org/). A common usage would be to mark the minimum
|
||||||
|
minor version you depend on, like `~1.2` (which allows anything up to, but not
|
||||||
|
including, 2.0). Since in theory there should be no backwards compatibility
|
||||||
|
breaks until 2.0, that works well. Another way of looking at it is that using
|
||||||
|
`~` specifies a minimum version, but allows the last digit specified to go up.
|
||||||
|
|
||||||
|
Example: `~1.2`
|
||||||
|
|
||||||
|
> **Note:** Though `2.0-beta.1` is strictly before `2.0`, a version constraint
|
||||||
|
> like `~1.2` would not install it. As said above `~1.2` only means the `.2`
|
||||||
|
> can change but the `1.` part is fixed.
|
||||||
|
|
||||||
|
> **Note:** The `~` operator has an exception on its behavior for the major
|
||||||
|
> release number. This means for example that `~1` is the same as `~1.0` as
|
||||||
|
> it will not allow the major number to increase trying to keep backwards
|
||||||
|
> compatibility.
|
||||||
|
|
||||||
|
### Caret
|
||||||
|
|
||||||
|
The `^` operator behaves very similarly but it sticks closer to semantic
|
||||||
|
versioning, and will always allow non-breaking updates. For example `^1.2.3`
|
||||||
|
is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should
|
||||||
|
break backwards compatibility. For pre-1.0 versions it also acts with safety
|
||||||
|
in mind and treats `^0.3` as `>=0.3.0 <0.4.0`.
|
||||||
|
|
||||||
|
This is the recommended operator for maximum interoperability when writing
|
||||||
|
library code.
|
||||||
|
|
||||||
|
Example: `^1.2.3`
|
||||||
|
|
||||||
|
## Stability
|
||||||
|
|
||||||
|
If you are using a constraint that does not explicitly define a stability,
|
||||||
|
Composer will default internally to `-dev` or `-stable`, depending on the
|
||||||
|
operator(s) used. This happens transparently.
|
||||||
|
|
||||||
|
If you wish to explicitly consider only the stable release in the comparison,
|
||||||
|
add the suffix `-stable`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Constraint | Internally
|
||||||
|
------------------- | ------------------------
|
||||||
|
`1.2.3` | `=1.2.3.0-stable`
|
||||||
|
`>1.2` | `>1.2.0.0-stable`
|
||||||
|
`>=1.2` | `>=1.2.0.0-dev`
|
||||||
|
`>=1.2-stable` | `>=1.2.0.0-stable`
|
||||||
|
`<1.3` | `<1.3.0.0-dev`
|
||||||
|
`<=1.3` | `<=1.3.0.0-stable`
|
||||||
|
`1 - 2` | `>=1.0.0.0-dev <3.0.0.0-dev`
|
||||||
|
`~1.3` | `>=1.3.0.0-dev <2.0.0.0-dev`
|
||||||
|
`1.4.*` | `>=1.4.0.0-dev <1.5.0.0-dev`
|
||||||
|
|
||||||
|
## Test version constraints
|
||||||
|
|
||||||
|
You can test version constraints using [semver.mwl.be](http://semver.mwl.be).
|
||||||
|
Fill in a package name and it will autofill the default version constraint
|
||||||
|
which Composer would add to your `composer.json` file. You can adjust the
|
||||||
|
version constraint and the tool will highlight all releases that match.
|
|
@ -12,6 +12,13 @@ resulting order in which the solver will try to install them.
|
||||||
|
|
||||||
The rules are to be applied in the order of these descriptions.
|
The rules are to be applied in the order of these descriptions.
|
||||||
|
|
||||||
|
### Repository priorities
|
||||||
|
|
||||||
|
Packages Repo1.Av1, Repo2.Av1
|
||||||
|
|
||||||
|
* priority(Repo1) >= priority(Repo2) => (Repo1.Av1, Repo2.Av1)
|
||||||
|
* priority(Repo1) < priority(Repo2) => (Repo2.Av1, Repo1.Av1)
|
||||||
|
|
||||||
### Package versions
|
### Package versions
|
||||||
|
|
||||||
Packages: Av1, Av2, Av3
|
Packages: Av1, Av2, Av3
|
||||||
|
@ -22,13 +29,6 @@ Request: install A
|
||||||
|
|
||||||
* (Av3)
|
* (Av3)
|
||||||
|
|
||||||
### Repository priorities
|
|
||||||
|
|
||||||
Packages Repo1.Av1, Repo2.Av1
|
|
||||||
|
|
||||||
* priority(Repo1) >= priority(Repo2) => (Repo1.Av1, Repo2.Av1)
|
|
||||||
* priority(Repo1) < priority(Repo2) => (Repo2.Av1, Repo1.Av1)
|
|
||||||
|
|
||||||
### Virtual Packages (provides)
|
### Virtual Packages (provides)
|
||||||
|
|
||||||
Packages Av1, Bv1
|
Packages Av1, Bv1
|
||||||
|
|
|
@ -27,6 +27,6 @@ If you really feel like you must do this, you have a few options:
|
||||||
in ZSH or `find vendor/ -type d -name ".git" -exec rm -rf {} \;` in Bash.
|
in ZSH or `find vendor/ -type d -name ".git" -exec rm -rf {} \;` in Bash.
|
||||||
but this means you will have to delete those dependencies from disk before
|
but this means you will have to delete those dependencies from disk before
|
||||||
running composer update.
|
running composer update.
|
||||||
4. Add a .gitignore rule (`vendor/.git`) to ignore all the vendor `.git` folders.
|
4. Add a .gitignore rule (`/vendor/**/.git`) to ignore all the vendor `.git` folders.
|
||||||
This approach does not require that you delete dependencies from disk prior to
|
This approach does not require that you delete dependencies from disk prior to
|
||||||
running a composer update.
|
running a composer update.
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Package version, see http://getcomposer.org/doc/04-schema.md#version for more info on valid schemes."
|
"description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes."
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -145,6 +145,11 @@
|
||||||
"type": ["string", "boolean"],
|
"type": ["string", "boolean"],
|
||||||
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
|
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
|
||||||
},
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "This is a hash of package name (keys) and version (values) that will be used to mock the platform packages on this machine.",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
"vendor-dir": {
|
"vendor-dir": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The location where all packages are installed, defaults to \"vendor\"."
|
"description": "The location where all packages are installed, defaults to \"vendor\"."
|
||||||
|
@ -181,6 +186,10 @@
|
||||||
"type": ["string", "integer"],
|
"type": ["string", "integer"],
|
||||||
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
|
||||||
},
|
},
|
||||||
|
"bin-compat": {
|
||||||
|
"enum": ["auto", "full"],
|
||||||
|
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
|
||||||
|
},
|
||||||
"discard-changes": {
|
"discard-changes": {
|
||||||
"type": ["string", "boolean"],
|
"type": ["string", "boolean"],
|
||||||
"description": "The default style of handling dirty updates, defaults to false and can be any of true, false or \"stash\"."
|
"description": "The default style of handling dirty updates, defaults to false and can be any of true, false or \"stash\"."
|
||||||
|
@ -211,6 +220,14 @@
|
||||||
"github-expose-hostname": {
|
"github-expose-hostname": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Defaults to true. If set to false, the OAuth tokens created to access the github API will have a date instead of the machine hostname."
|
"description": "Defaults to true. If set to false, the OAuth tokens created to access the github API will have a date instead of the machine hostname."
|
||||||
|
},
|
||||||
|
"archive-format": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The default archiving format when not provided on cli, defaults to \"tar\"."
|
||||||
|
},
|
||||||
|
"archive-dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The default archive path when not provided on cli, defaults to \".\"."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -240,6 +257,10 @@
|
||||||
"files": {
|
"files": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "This is an array of files that are always required on every request."
|
"description": "This is an array of files that are always required on every request."
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "This is an array of patterns to exclude from autoload classmap generation. (e.g. \"exclude-from-classmap\": [\"/test/\", \"/tests/\", \"/Tests/\"]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -289,7 +310,7 @@
|
||||||
},
|
},
|
||||||
"prefer-stable": {
|
"prefer-stable": {
|
||||||
"type": ["boolean"],
|
"type": ["boolean"],
|
||||||
"description": "If set to true, stable packages will be prefered to dev packages when possible, even if the minimum-stability allows unstable packages."
|
"description": "If set to true, stable packages will be preferred to dev packages when possible, even if the minimum-stability allows unstable packages."
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"type": ["array"],
|
"type": ["array"],
|
||||||
|
@ -385,17 +406,17 @@
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL to the Issue Tracker.",
|
"description": "URL to the issue tracker.",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
},
|
},
|
||||||
"forum": {
|
"forum": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL to the Forum.",
|
"description": "URL to the forum.",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
},
|
},
|
||||||
"wiki": {
|
"wiki": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL to the Wiki.",
|
"description": "URL to the wiki.",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
},
|
},
|
||||||
"irc": {
|
"irc": {
|
||||||
|
@ -407,12 +428,17 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL to browse or download the sources.",
|
"description": "URL to browse or download the sources.",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"docs": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL to the documentation.",
|
||||||
|
"format": "uri"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"non-feature-branches": {
|
"non-feature-branches": {
|
||||||
"type": ["array"],
|
"type": ["array"],
|
||||||
"description": "A set of string or regex patterns for non-numeric branch names that will not be handles as feature branches.",
|
"description": "A set of string or regex patterns for non-numeric branch names that will not be handled as feature branches.",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
[
|
|
||||||
"Glide", "Abstyles", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0",
|
|
||||||
"AMPAS", "APL-1.0", "Adobe-Glyph", "APAFML", "Adobe-2006", "AGPL-1.0",
|
|
||||||
"Afmparse", "Aladdin", "ADSL", "AMDPLPA", "ANTLR-PD", "Apache-1.0",
|
|
||||||
"Apache-1.1", "Apache-2.0", "AML", "APSL-1.0", "APSL-1.1", "APSL-1.2",
|
|
||||||
"APSL-2.0", "Artistic-1.0", "Artistic-1.0-Perl", "Artistic-1.0-cl8",
|
|
||||||
"Artistic-2.0", "AAL", "Bahyph", "Barr", "Beerware", "BitTorrent-1.0",
|
|
||||||
"BitTorrent-1.1", "BSL-1.0", "Borceux", "BSD-2-Clause",
|
|
||||||
"BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-3-Clause",
|
|
||||||
"BSD-3-Clause-Clear", "BSD-4-Clause", "BSD-Protection",
|
|
||||||
"BSD-3-Clause-Attribution", "BSD-4-Clause-UC", "bzip2-1.0.5", "bzip2-1.0.6",
|
|
||||||
"Caldera", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C",
|
|
||||||
"ClArtistic", "MIT-CMU", "CNRI-Python", "CNRI-Python-GPL-Compatible",
|
|
||||||
"CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1",
|
|
||||||
"Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0",
|
|
||||||
"CC-BY-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0",
|
|
||||||
"CC-BY-ND-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5",
|
|
||||||
"CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0",
|
|
||||||
"CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0",
|
|
||||||
"CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0",
|
|
||||||
"CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0",
|
|
||||||
"CC-BY-SA-4.0", "CC0-1.0", "Crossword", "CUA-OPL-1.0", "Cube", "D-FSL-1.0",
|
|
||||||
"diffmark", "WTFPL", "DOC", "Dotseqn", "DSDP", "dvipdfm", "EPL-1.0",
|
|
||||||
"eCos-2.0", "ECL-1.0", "ECL-2.0", "eGenix", "EFL-1.0", "EFL-2.0",
|
|
||||||
"MIT-advertising", "MIT-enna", "Entessa", "ErlPL-1.1", "EUDatagrid",
|
|
||||||
"EUPL-1.0", "EUPL-1.1", "Eurosym", "Fair", "MIT-feh", "Frameworx-1.0",
|
|
||||||
"FTL", "FSFUL", "FSFULLR", "Giftware", "GL2PS", "Glulxe", "AGPL-3.0",
|
|
||||||
"GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0",
|
|
||||||
"GPL-2.0+", "GPL-2.0-with-autoconf-exception",
|
|
||||||
"GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception",
|
|
||||||
"GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0",
|
|
||||||
"GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception",
|
|
||||||
"LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+",
|
|
||||||
"gnuplot", "gSOAP-1.3b", "HaskellReport", "HPND", "IBM-pibs", "IPL-1.0",
|
|
||||||
"ImageMagick", "iMatix", "Imlib2", "IJG", "Intel-ACPI", "Intel", "IPA",
|
|
||||||
"ISC", "JasPer-2.0", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1",
|
|
||||||
"LPPL-1.2", "LPPL-1.3c", "Latex2e", "BSD-3-Clause-LBNL", "Leptonica",
|
|
||||||
"Libpng", "libtiff", "LPL-1.02", "LPL-1.0", "MakeIndex", "MTLL", "MS-PL",
|
|
||||||
"MS-RL", "MirOS", "MITNFA", "MIT", "Motosoto", "MPL-1.0", "MPL-1.1",
|
|
||||||
"MPL-2.0", "MPL-2.0-no-copyleft-exception", "mpich2", "Multics", "Mup",
|
|
||||||
"NASA-1.3", "Naumen", "NBPL-1.0", "NetCDF", "NGPL", "NOSL", "NPL-1.0",
|
|
||||||
"NPL-1.1", "Newsletr", "NLPL", "Nokia", "NPOSL-3.0", "Noweb", "NRL", "NTP",
|
|
||||||
"Nunit", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2",
|
|
||||||
"OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0",
|
|
||||||
"OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3",
|
|
||||||
"OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OML", "OPL-1.0",
|
|
||||||
"OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8",
|
|
||||||
"OpenSSL", "PHP-3.0", "PHP-3.01", "Plexus", "PostgreSQL", "psfrag",
|
|
||||||
"psutils", "Python-2.0", "QPL-1.0", "Qhull", "Rdisc", "RPSL-1.0", "RPL-1.1",
|
|
||||||
"RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA",
|
|
||||||
"SWL", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1",
|
|
||||||
"SimPL-2.0", "Sleepycat", "SNIA", "SMLNJ", "StandardML-NJ",
|
|
||||||
"SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", "TCL",
|
|
||||||
"Unlicense", "TMate", "TORQUE-1.1", "TOSL", "Unicode-TOU", "NCSA", "Vim",
|
|
||||||
"VOSTROM", "VSL-1.0", "W3C", "Wsuipa", "WXwindows", "Xnet", "X11", "Xerox",
|
|
||||||
"XFree86-1.1", "xinetd", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed",
|
|
||||||
"Zend-2.0", "Zimbra-1.3", "Zlib", "zlib-acknowledgement", "ZPL-1.1",
|
|
||||||
"ZPL-2.0", "ZPL-2.1"
|
|
||||||
]
|
|
|
@ -38,8 +38,16 @@ class AutoloadGenerator
|
||||||
*/
|
*/
|
||||||
private $io;
|
private $io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
private $devMode = false;
|
private $devMode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
|
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
|
||||||
{
|
{
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
|
@ -51,8 +59,23 @@ class AutoloadGenerator
|
||||||
$this->devMode = (boolean) $devMode;
|
$this->devMode = (boolean) $devMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not generated autoloader considers the class map
|
||||||
|
* authoritative.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = (boolean) $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
|
public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
|
||||||
{
|
{
|
||||||
|
if ($this->classMapAuthoritative) {
|
||||||
|
// Force scanPsr0Packages when classmap is authoritative
|
||||||
|
$scanPsr0Packages = true;
|
||||||
|
}
|
||||||
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array(
|
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array(
|
||||||
'optimize' => (bool) $scanPsr0Packages,
|
'optimize' => (bool) $scanPsr0Packages,
|
||||||
));
|
));
|
||||||
|
@ -63,7 +86,6 @@ class AutoloadGenerator
|
||||||
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
|
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
|
||||||
$useGlobalIncludePath = (bool) $config->get('use-include-path');
|
$useGlobalIncludePath = (bool) $config->get('use-include-path');
|
||||||
$prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
|
$prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
|
||||||
$classMapAuthoritative = $config->get('classmap-authoritative');
|
|
||||||
$targetDir = $vendorPath.'/'.$targetDir;
|
$targetDir = $vendorPath.'/'.$targetDir;
|
||||||
$filesystem->ensureDirectoryExists($targetDir);
|
$filesystem->ensureDirectoryExists($targetDir);
|
||||||
|
|
||||||
|
@ -171,40 +193,48 @@ EOF;
|
||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$blacklist = null;
|
||||||
|
if (!empty($autoloads['exclude-from-classmap'])) {
|
||||||
|
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
|
||||||
|
}
|
||||||
|
|
||||||
// flatten array
|
// flatten array
|
||||||
$classMap = array();
|
$classMap = array();
|
||||||
if ($scanPsr0Packages) {
|
if ($scanPsr0Packages) {
|
||||||
|
$namespacesToScan = array();
|
||||||
|
|
||||||
// Scan the PSR-0/4 directories for class files, and add them to the class map
|
// Scan the PSR-0/4 directories for class files, and add them to the class map
|
||||||
foreach (array('psr-0', 'psr-4') as $psrType) {
|
foreach (array('psr-0', 'psr-4') as $psrType) {
|
||||||
foreach ($autoloads[$psrType] as $namespace => $paths) {
|
foreach ($autoloads[$psrType] as $namespace => $paths) {
|
||||||
foreach ($paths as $dir) {
|
$namespacesToScan[$namespace][] = array('paths' => $paths, 'type' => $psrType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
krsort($namespacesToScan);
|
||||||
|
|
||||||
|
foreach ($namespacesToScan as $namespace => $groups) {
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$psrType = $group['type'];
|
||||||
|
foreach ($group['paths'] as $dir) {
|
||||||
$dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir);
|
$dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir);
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$whitelist = sprintf(
|
// $whitelist = sprintf(
|
||||||
'{%s/%s.+(?<!(?<!/)Test\.php)$}',
|
// '{%s/%s.+$}',
|
||||||
preg_quote($dir),
|
// preg_quote($dir),
|
||||||
($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : ''
|
// ($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : ''
|
||||||
);
|
// );
|
||||||
|
|
||||||
$namespaceFilter = $namespace === '' ? null : $namespace;
|
$namespaceFilter = $namespace === '' ? null : $namespace;
|
||||||
foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io, $namespaceFilter) as $class => $path) {
|
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $classMap);
|
||||||
if (!isset($classMap[$class])) {
|
|
||||||
$path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
|
|
||||||
$classMap[$class] = $path.",\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($autoloads['classmap'] as $dir) {
|
foreach ($autoloads['classmap'] as $dir) {
|
||||||
foreach (ClassMapGenerator::createMap($dir, null, $this->io) as $class => $path) {
|
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, $classMap);
|
||||||
$path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
|
|
||||||
$classMap[$class] = $path.",\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($classMap);
|
ksort($classMap);
|
||||||
|
@ -214,35 +244,66 @@ EOF;
|
||||||
$classmapFile .= ");\n";
|
$classmapFile .= ");\n";
|
||||||
|
|
||||||
if (!$suffix) {
|
if (!$suffix) {
|
||||||
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
|
if (!$config->get('autoloader-suffix') && is_readable($vendorPath.'/autoload.php')) {
|
||||||
|
$content = file_get_contents($vendorPath.'/autoload.php');
|
||||||
|
if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) {
|
||||||
|
$suffix = $match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$suffix) {
|
||||||
|
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
|
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
|
||||||
file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
|
file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
|
||||||
file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
|
file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
|
||||||
if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
$includePathFilePath = $targetDir.'/include_paths.php';
|
||||||
file_put_contents($targetDir.'/include_paths.php', $includePathFile);
|
if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
||||||
|
file_put_contents($includePathFilePath, $includePathFileContents);
|
||||||
|
} elseif (file_exists($includePathFilePath)) {
|
||||||
|
unlink($includePathFilePath);
|
||||||
}
|
}
|
||||||
if ($includeFilesFile = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
$includeFilesFilePath = $targetDir.'/autoload_files.php';
|
||||||
file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
|
if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
||||||
|
file_put_contents($includeFilesFilePath, $includeFilesFileContents);
|
||||||
|
} elseif (file_exists($includeFilesFilePath)) {
|
||||||
|
unlink($includeFilesFilePath);
|
||||||
}
|
}
|
||||||
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
|
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
|
||||||
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative));
|
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader));
|
||||||
|
|
||||||
// use stream_copy_to_stream instead of copy
|
$this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
|
||||||
// to work around https://bugs.php.net/bug.php?id=64634
|
$this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
|
||||||
$sourceLoader = fopen(__DIR__.'/ClassLoader.php', 'r');
|
|
||||||
$targetLoader = fopen($targetDir.'/ClassLoader.php', 'w+');
|
|
||||||
stream_copy_to_stream($sourceLoader, $targetLoader);
|
|
||||||
fclose($sourceLoader);
|
|
||||||
fclose($targetLoader);
|
|
||||||
unset($sourceLoader, $targetLoader);
|
|
||||||
|
|
||||||
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
|
$this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
|
||||||
'optimize' => (bool) $scanPsr0Packages,
|
'optimize' => (bool) $scanPsr0Packages,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, array $classMap = array())
|
||||||
|
{
|
||||||
|
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter) as $class => $path) {
|
||||||
|
$pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
|
||||||
|
if (!isset($classMap[$class])) {
|
||||||
|
$classMap[$class] = $pathCode;
|
||||||
|
} elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) {
|
||||||
|
$this->io->writeError(
|
||||||
|
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
|
||||||
|
' was found in both "'.str_replace(array('$vendorDir . \'', "',\n"), array($vendorPath, ''), $classMap[$class]).'" and "'.$path.'", the first will be used.</warning>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null)
|
||||||
|
{
|
||||||
|
return ClassMapGenerator::createMap($dir, $blacklist, $this->io, $namespaceFilter);
|
||||||
|
}
|
||||||
|
|
||||||
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
|
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
|
||||||
{
|
{
|
||||||
// build package => install path map
|
// build package => install path map
|
||||||
|
@ -301,13 +362,20 @@ EOF;
|
||||||
|
|
||||||
$psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
|
$psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
|
||||||
$psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
|
$psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
|
||||||
$classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage);
|
$classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $mainPackage);
|
||||||
$files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
|
$files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
|
||||||
|
$exclude = $this->parseAutoloadsType($sortedPackageMap, 'exclude-from-classmap', $mainPackage);
|
||||||
|
|
||||||
krsort($psr0);
|
krsort($psr0);
|
||||||
krsort($psr4);
|
krsort($psr4);
|
||||||
|
|
||||||
return array('psr-0' => $psr0, 'psr-4' => $psr4, 'classmap' => $classmap, 'files' => $files);
|
return array(
|
||||||
|
'psr-0' => $psr0,
|
||||||
|
'psr-4' => $psr4,
|
||||||
|
'classmap' => $classmap,
|
||||||
|
'files' => $files,
|
||||||
|
'exclude-from-classmap' => $exclude
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -332,6 +400,16 @@ EOF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($autoloads['classmap'])) {
|
||||||
|
foreach ($autoloads['classmap'] as $dir) {
|
||||||
|
try {
|
||||||
|
$loader->addClassMap($this->generateClassMap($dir));
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
$this->io->writeError('<warning>'.$e->getMessage().'</warning>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $loader;
|
return $loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +522,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
|
||||||
AUTOLOAD;
|
AUTOLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative)
|
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
|
||||||
{
|
{
|
||||||
// TODO the class ComposerAutoloaderInit should be revert to a closure
|
// TODO the class ComposerAutoloaderInit should be revert to a closure
|
||||||
// when APC has been fixed:
|
// when APC has been fixed:
|
||||||
|
@ -521,7 +599,7 @@ PSR4;
|
||||||
CLASSMAP;
|
CLASSMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($classMapAuthoritative) {
|
if ($this->classMapAuthoritative) {
|
||||||
$file .= <<<'CLASSMAPAUTHORITATIVE'
|
$file .= <<<'CLASSMAPAUTHORITATIVE'
|
||||||
$loader->setClassMapAuthoritative(true);
|
$loader->setClassMapAuthoritative(true);
|
||||||
|
|
||||||
|
@ -601,7 +679,7 @@ FOOTER;
|
||||||
|
|
||||||
foreach ($autoload[$type] as $namespace => $paths) {
|
foreach ($autoload[$type] as $namespace => $paths) {
|
||||||
foreach ((array) $paths as $path) {
|
foreach ((array) $paths as $path) {
|
||||||
if (($type === 'files' || $type === 'classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) {
|
if (($type === 'files' || $type === 'classmap' || $type === 'exclude-from-classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) {
|
||||||
// remove target-dir from file paths of the root package
|
// remove target-dir from file paths of the root package
|
||||||
if ($package === $mainPackage) {
|
if ($package === $mainPackage) {
|
||||||
$targetDir = str_replace('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $package->getTargetDir())));
|
$targetDir = str_replace('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $package->getTargetDir())));
|
||||||
|
@ -612,6 +690,18 @@ FOOTER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($type === 'exclude-from-classmap') {
|
||||||
|
// first escape user input
|
||||||
|
$path = preg_quote(trim(strtr($path, '\\', '/'), '/'));
|
||||||
|
|
||||||
|
// add support for wildcards * and **
|
||||||
|
$path = str_replace('\\*\\*', '.+?', $path);
|
||||||
|
$path = str_replace('\\*', '[^/]+?', $path);
|
||||||
|
|
||||||
|
$autoloads[] = empty($installPath) ? preg_quote(strtr(getcwd(), '\\', '/')) . '/' . $path : preg_quote($installPath) . '/' . $path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path;
|
$relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path;
|
||||||
|
|
||||||
if ($type === 'files' || $type === 'classmap') {
|
if ($type === 'files' || $type === 'classmap') {
|
||||||
|
@ -718,4 +808,20 @@ FOOTER;
|
||||||
|
|
||||||
return $sortedPackageMap;
|
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)
|
||||||
|
{
|
||||||
|
$source = fopen($source, 'r');
|
||||||
|
$target = fopen($target, 'w+');
|
||||||
|
|
||||||
|
stream_copy_to_stream($source, $target);
|
||||||
|
fclose($source);
|
||||||
|
fclose($target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<?php
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is copied from the Symfony package.
|
* This file is copied from the Symfony package.
|
||||||
*
|
*
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @license MIT
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
namespace Composer\Autoload;
|
||||||
|
@ -45,15 +50,14 @@ class ClassMapGenerator
|
||||||
* Iterate over all files in the given directory searching for classes
|
* Iterate over all files in the given directory searching for classes
|
||||||
*
|
*
|
||||||
* @param \Iterator|string $path The path to search in or an iterator
|
* @param \Iterator|string $path The path to search in or an iterator
|
||||||
* @param string $whitelist Regex that matches against the file path
|
* @param string $blacklist Regex that matches against the file path that exclude from the classmap.
|
||||||
* @param IOInterface $io IO object
|
* @param IOInterface $io IO object
|
||||||
* @param string $namespace Optional namespace prefix to filter by
|
* @param string $namespace Optional namespace prefix to filter by
|
||||||
*
|
*
|
||||||
* @return array A class map array
|
|
||||||
*
|
|
||||||
* @throws \RuntimeException When the path is neither an existing file nor directory
|
* @throws \RuntimeException When the path is neither an existing file nor directory
|
||||||
|
* @return array A class map array
|
||||||
*/
|
*/
|
||||||
public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null)
|
public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null)
|
||||||
{
|
{
|
||||||
if (is_string($path)) {
|
if (is_string($path)) {
|
||||||
if (is_file($path)) {
|
if (is_file($path)) {
|
||||||
|
@ -77,7 +81,7 @@ class ClassMapGenerator
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) {
|
if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +95,7 @@ class ClassMapGenerator
|
||||||
|
|
||||||
if (!isset($map[$class])) {
|
if (!isset($map[$class])) {
|
||||||
$map[$class] = $filePath;
|
$map[$class] = $filePath;
|
||||||
} elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
|
} elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
|
||||||
$io->writeError(
|
$io->writeError(
|
||||||
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
|
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
|
||||||
' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'
|
' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'
|
||||||
|
@ -112,7 +116,10 @@ class ClassMapGenerator
|
||||||
*/
|
*/
|
||||||
private static function findClasses($path)
|
private static function findClasses($path)
|
||||||
{
|
{
|
||||||
$traits = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait';
|
$extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait';
|
||||||
|
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) {
|
||||||
|
$extraTypes .= '|enum';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$contents = @php_strip_whitespace($path);
|
$contents = @php_strip_whitespace($path);
|
||||||
|
@ -129,14 +136,14 @@ class ClassMapGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
// return early if there is no chance of matching anything in this file
|
// return early if there is no chance of matching anything in this file
|
||||||
if (!preg_match('{\b(?:class|interface'.$traits.')\s}i', $contents)) {
|
if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip heredocs/nowdocs
|
// strip heredocs/nowdocs
|
||||||
$contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
|
$contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
|
||||||
// strip strings
|
// strip strings
|
||||||
$contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}s', 'null', $contents);
|
$contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
|
||||||
// strip leading non-php code if needed
|
// strip leading non-php code if needed
|
||||||
if (substr($contents, 0, 2) !== '<?') {
|
if (substr($contents, 0, 2) !== '<?') {
|
||||||
$contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
|
$contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
|
||||||
|
@ -154,8 +161,8 @@ class ClassMapGenerator
|
||||||
|
|
||||||
preg_match_all('{
|
preg_match_all('{
|
||||||
(?:
|
(?:
|
||||||
\b(?<![\$:>])(?P<type>class|interface'.$traits.') \s+ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*)
|
\b(?<![\$:>])(?P<type>class|interface'.$extraTypes.') \s++ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+)
|
||||||
| \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;]
|
| \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+\\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;]
|
||||||
)
|
)
|
||||||
}ix', $contents, $matches);
|
}ix', $contents, $matches);
|
||||||
|
|
||||||
|
@ -170,6 +177,12 @@ class ClassMapGenerator
|
||||||
if ($name[0] === ':') {
|
if ($name[0] === ':') {
|
||||||
// This is an XHP class, https://github.com/facebook/xhp
|
// This is an XHP class, https://github.com/facebook/xhp
|
||||||
$name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
|
$name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
|
||||||
|
} elseif ($matches['type'][$i] === 'enum') {
|
||||||
|
// In Hack, something like:
|
||||||
|
// enum Foo: int { HERP = '123'; }
|
||||||
|
// The regex above captures the colon, which isn't part of
|
||||||
|
// the class name.
|
||||||
|
$name = rtrim($name, ':');
|
||||||
}
|
}
|
||||||
$classes[] = ltrim($namespace . $name, '\\');
|
$classes[] = ltrim($namespace . $name, '\\');
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,12 @@ class Cache
|
||||||
$this->whitelist = $whitelist;
|
$this->whitelist = $whitelist;
|
||||||
$this->filesystem = $filesystem ?: new Filesystem();
|
$this->filesystem = $filesystem ?: new Filesystem();
|
||||||
|
|
||||||
if (!is_dir($this->root)) {
|
if (
|
||||||
if (!@mkdir($this->root, 0777, true)) {
|
(!is_dir($this->root) && !@mkdir($this->root, 0777, true))
|
||||||
$this->enabled = false;
|
|| !is_writable($this->root)
|
||||||
}
|
) {
|
||||||
|
$this->io->writeError('<warning>Cannot create cache directory ' . $this->root . ', or directory is not writable. Proceeding without cache</warning>');
|
||||||
|
$this->enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +88,9 @@ class Cache
|
||||||
try {
|
try {
|
||||||
return file_put_contents($this->root . $file, $contents);
|
return file_put_contents($this->root . $file, $contents);
|
||||||
} catch (\ErrorException $e) {
|
} catch (\ErrorException $e) {
|
||||||
|
if ($this->io->isDebug()) {
|
||||||
|
$this->io->writeError('<warning>Failed to write into cache: '.$e->getMessage().'</warning>');
|
||||||
|
}
|
||||||
if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
|
if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
|
||||||
// Remove partial file.
|
// Remove partial file.
|
||||||
unlink($this->root . $file);
|
unlink($this->root . $file);
|
||||||
|
@ -119,8 +124,10 @@ class Cache
|
||||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||||
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
||||||
|
|
||||||
if ($this->io->isDebug()) {
|
if (!file_exists($source)) {
|
||||||
$this->io->writeError('Writing '.$this->root . $file.' into cache');
|
$this->io->writeError('<error>'.$source.' does not exist, can not write into cache</error>');
|
||||||
|
} elseif ($this->io->isDebug()) {
|
||||||
|
$this->io->writeError('Writing '.$this->root . $file.' into cache from '.$source);
|
||||||
}
|
}
|
||||||
|
|
||||||
return copy($source, $this->root . $file);
|
return copy($source, $this->root . $file);
|
||||||
|
@ -136,7 +143,13 @@ class Cache
|
||||||
{
|
{
|
||||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||||
if ($this->enabled && file_exists($this->root . $file)) {
|
if ($this->enabled && file_exists($this->root . $file)) {
|
||||||
touch($this->root . $file);
|
try {
|
||||||
|
touch($this->root . $file, filemtime($this->root . $file), time());
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
// fallback in case the above failed due to incorrect ownership
|
||||||
|
// see https://github.com/composer/composer/issues/4070
|
||||||
|
touch($this->root . $file);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->writeError('Reading '.$this->root . $file.' from cache');
|
$this->io->writeError('Reading '.$this->root . $file.' from cache');
|
||||||
|
|
|
@ -37,7 +37,7 @@ EOT
|
||||||
$this->getIO()->write(<<<EOT
|
$this->getIO()->write(<<<EOT
|
||||||
<info>Composer - Package Management for PHP</info>
|
<info>Composer - Package Management for PHP</info>
|
||||||
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
|
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
|
||||||
See http://getcomposer.org/ for more information.</comment>
|
See https://getcomposer.org/ for more information.</comment>
|
||||||
EOT
|
EOT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,12 @@ namespace Composer\Command;
|
||||||
|
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\DependencyResolver\Pool;
|
use Composer\Config;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Script\ScriptEvents;
|
use Composer\Script\ScriptEvents;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Util\Filesystem;
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -41,8 +40,10 @@ class ArchiveCommand extends Command
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'),
|
new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'),
|
||||||
new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'),
|
new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'),
|
||||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip', 'tar'),
|
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip'),
|
||||||
new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory', '.'),
|
new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory'),
|
||||||
|
new InputOption('file', false, InputOption::VALUE_REQUIRED, 'Write the archive with the given file name.'
|
||||||
|
.' Note that the format will be appended.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The <info>archive</info> command creates an archive of the specified format
|
The <info>archive</info> command creates an archive of the specified format
|
||||||
|
@ -58,6 +59,7 @@ EOT
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
$config = Factory::createConfig();
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
|
||||||
|
@ -65,12 +67,21 @@ EOT
|
||||||
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null === $input->getOption('format')) {
|
||||||
|
$input->setOption('format', $config->get('archive-format'));
|
||||||
|
}
|
||||||
|
if (null === $input->getOption('dir')) {
|
||||||
|
$input->setOption('dir', $config->get('archive-dir'));
|
||||||
|
}
|
||||||
|
|
||||||
$returnCode = $this->archive(
|
$returnCode = $this->archive(
|
||||||
$this->getIO(),
|
$this->getIO(),
|
||||||
|
$config,
|
||||||
$input->getArgument('package'),
|
$input->getArgument('package'),
|
||||||
$input->getArgument('version'),
|
$input->getArgument('version'),
|
||||||
$input->getOption('format'),
|
$input->getOption('format'),
|
||||||
$input->getOption('dir')
|
$input->getOption('dir'),
|
||||||
|
$input->getOption('file')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (0 === $returnCode && $composer) {
|
if (0 === $returnCode && $composer) {
|
||||||
|
@ -80,9 +91,8 @@ EOT
|
||||||
return $returnCode;
|
return $returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function archive(IOInterface $io, $packageName = null, $version = null, $format = 'tar', $dest = '.')
|
protected function archive(IOInterface $io, Config $config, $packageName = null, $version = null, $format = 'tar', $dest = '.', $fileName = null)
|
||||||
{
|
{
|
||||||
$config = Factory::createConfig();
|
|
||||||
$factory = new Factory;
|
$factory = new Factory;
|
||||||
$downloadManager = $factory->createDownloadManager($io, $config);
|
$downloadManager = $factory->createDownloadManager($io, $config);
|
||||||
$archiveManager = $factory->createArchiveManager($config, $downloadManager);
|
$archiveManager = $factory->createArchiveManager($config, $downloadManager);
|
||||||
|
@ -97,8 +107,13 @@ EOT
|
||||||
$package = $this->getComposer()->getPackage();
|
$package = $this->getComposer()->getPackage();
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->writeError('<info>Creating the archive.</info>');
|
$io->writeError('<info>Creating the archive into "'.$dest.'".</info>');
|
||||||
$archiveManager->archive($package, $format, $dest);
|
$packagePath = $archiveManager->archive($package, $format, $dest, $fileName);
|
||||||
|
$fs = new Filesystem;
|
||||||
|
$shortPath = $fs->findShortestPath(getcwd(), $packagePath, true);
|
||||||
|
|
||||||
|
$io->writeError('Created: ', false);
|
||||||
|
$io->write(strlen($shortPath) < strlen($packagePath) ? $shortPath : $packagePath);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -109,19 +124,14 @@ EOT
|
||||||
|
|
||||||
if ($composer = $this->getComposer(false)) {
|
if ($composer = $this->getComposer(false)) {
|
||||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
$repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
|
$repo = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
|
||||||
} else {
|
} else {
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
||||||
$io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
|
$io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
|
||||||
$repos = new CompositeRepository($defaultRepos);
|
$repo = new CompositeRepository($defaultRepos);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pool = new Pool();
|
$packages = $repo->findPackages($packageName, $version);
|
||||||
$pool->addRepository($repos);
|
|
||||||
|
|
||||||
$parser = new VersionParser();
|
|
||||||
$constraint = ($version) ? $parser->parseConstraints($version) : null;
|
|
||||||
$packages = $pool->whatProvides($packageName, $constraint, true);
|
|
||||||
|
|
||||||
if (count($packages) > 1) {
|
if (count($packages) > 1) {
|
||||||
$package = reset($packages);
|
$package = reset($packages);
|
||||||
|
|
|
@ -66,7 +66,7 @@ class ConfigCommand extends Command
|
||||||
new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'),
|
new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'),
|
||||||
new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
|
new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
|
||||||
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
|
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', 'composer.json'),
|
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('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
|
||||||
new InputArgument('setting-key', null, 'Setting key'),
|
new InputArgument('setting-key', null, 'Setting key'),
|
||||||
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
|
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
|
||||||
|
@ -129,7 +129,7 @@ EOT
|
||||||
{
|
{
|
||||||
parent::initialize($input, $output);
|
parent::initialize($input, $output);
|
||||||
|
|
||||||
if ($input->getOption('global') && 'composer.json' !== $input->getOption('file')) {
|
if ($input->getOption('global') && null !== $input->getOption('file')) {
|
||||||
throw new \RuntimeException('--file and --global can not be combined');
|
throw new \RuntimeException('--file and --global can not be combined');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,14 +139,19 @@ EOT
|
||||||
// passed in a file to use
|
// passed in a file to use
|
||||||
$configFile = $input->getOption('global')
|
$configFile = $input->getOption('global')
|
||||||
? ($this->config->get('home') . '/config.json')
|
? ($this->config->get('home') . '/config.json')
|
||||||
: $input->getOption('file');
|
: ($input->getOption('file') ?: trim(getenv('COMPOSER')) ?: 'composer.json');
|
||||||
|
|
||||||
|
// create global composer.json if this was invoked using `composer global config`
|
||||||
|
if ($configFile === 'composer.json' && !file_exists($configFile) && realpath(getcwd()) === realpath($this->config->get('home'))) {
|
||||||
|
file_put_contents($configFile, "{\n}\n");
|
||||||
|
}
|
||||||
|
|
||||||
$this->configFile = new JsonFile($configFile);
|
$this->configFile = new JsonFile($configFile);
|
||||||
$this->configSource = new JsonConfigSource($this->configFile);
|
$this->configSource = new JsonConfigSource($this->configFile);
|
||||||
|
|
||||||
$authConfigFile = $input->getOption('global')
|
$authConfigFile = $input->getOption('global')
|
||||||
? ($this->config->get('home') . '/auth.json')
|
? ($this->config->get('home') . '/auth.json')
|
||||||
: dirname(realpath($input->getOption('file'))) . '/auth.json';
|
: dirname(realpath($configFile)) . '/auth.json';
|
||||||
|
|
||||||
$this->authConfigFile = new JsonFile($authConfigFile);
|
$this->authConfigFile = new JsonFile($authConfigFile);
|
||||||
$this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
|
$this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
|
||||||
|
@ -233,17 +238,19 @@ EOT
|
||||||
} elseif (strpos($settingKey, '.')) {
|
} elseif (strpos($settingKey, '.')) {
|
||||||
$bits = explode('.', $settingKey);
|
$bits = explode('.', $settingKey);
|
||||||
$data = $data['config'];
|
$data = $data['config'];
|
||||||
|
$match = false;
|
||||||
foreach ($bits as $bit) {
|
foreach ($bits as $bit) {
|
||||||
if (isset($data[$bit])) {
|
$key = isset($key) ? $key.'.'.$bit : $bit;
|
||||||
$data = $data[$bit];
|
$match = false;
|
||||||
} elseif (isset($data[implode('.', $bits)])) {
|
if (isset($data[$key])) {
|
||||||
// last bit can contain domain names and such so try to join whatever is left if it exists
|
$match = true;
|
||||||
$data = $data[implode('.', $bits)];
|
$data = $data[$key];
|
||||||
break;
|
unset($key);
|
||||||
} else {
|
|
||||||
throw new \RuntimeException($settingKey.' is not defined');
|
|
||||||
}
|
}
|
||||||
array_shift($bits);
|
}
|
||||||
|
|
||||||
|
if (!$match) {
|
||||||
|
throw new \RuntimeException($settingKey.' is not defined.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $data;
|
$value = $data;
|
||||||
|
@ -273,7 +280,7 @@ EOT
|
||||||
'use-include-path' => array($booleanValidator, $booleanNormalizer),
|
'use-include-path' => array($booleanValidator, $booleanNormalizer),
|
||||||
'preferred-install' => array(
|
'preferred-install' => array(
|
||||||
function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); },
|
function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); },
|
||||||
function ($val) { return $val; }
|
function ($val) { return $val; },
|
||||||
),
|
),
|
||||||
'store-auths' => array(
|
'store-auths' => array(
|
||||||
function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); },
|
function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); },
|
||||||
|
@ -283,11 +290,13 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
return $val !== 'false' && (bool) $val;
|
return $val !== 'false' && (bool) $val;
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
'notify-on-install' => array($booleanValidator, $booleanNormalizer),
|
'notify-on-install' => array($booleanValidator, $booleanNormalizer),
|
||||||
'vendor-dir' => array('is_string', function ($val) { return $val; }),
|
'vendor-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
'bin-dir' => array('is_string', function ($val) { return $val; }),
|
'bin-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
|
'archive-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
|
'archive-format' => array('is_string', function ($val) { return $val; }),
|
||||||
'cache-dir' => array('is_string', function ($val) { return $val; }),
|
'cache-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
'cache-files-dir' => array('is_string', function ($val) { return $val; }),
|
'cache-files-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
'cache-repo-dir' => array('is_string', function ($val) { return $val; }),
|
'cache-repo-dir' => array('is_string', function ($val) { return $val; }),
|
||||||
|
@ -296,6 +305,10 @@ EOT
|
||||||
'cache-files-ttl' => array('is_numeric', 'intval'),
|
'cache-files-ttl' => array('is_numeric', 'intval'),
|
||||||
'cache-files-maxsize' => array(
|
'cache-files-maxsize' => array(
|
||||||
function ($val) { return preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $val) > 0; },
|
function ($val) { return preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $val) > 0; },
|
||||||
|
function ($val) { return $val; },
|
||||||
|
),
|
||||||
|
'bin-compat' => array(
|
||||||
|
function ($val) { return in_array($val, array('auto', 'full')); },
|
||||||
function ($val) { return $val; }
|
function ($val) { return $val; }
|
||||||
),
|
),
|
||||||
'discard-changes' => array(
|
'discard-changes' => array(
|
||||||
|
@ -306,7 +319,7 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
return $val !== 'false' && (bool) $val;
|
return $val !== 'false' && (bool) $val;
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
|
'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
|
||||||
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
|
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
|
||||||
|
@ -331,7 +344,7 @@ EOT
|
||||||
},
|
},
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
return $vals;
|
return $vals;
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
'github-domains' => array(
|
'github-domains' => array(
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
|
@ -343,7 +356,7 @@ EOT
|
||||||
},
|
},
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
return $vals;
|
return $vals;
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -410,6 +423,15 @@ EOT
|
||||||
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
|
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle platform
|
||||||
|
if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) {
|
||||||
|
if ($input->getOption('unset')) {
|
||||||
|
return $this->configSource->removeConfigSetting($settingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->configSource->addConfigSetting($settingKey, $values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
// handle github-oauth
|
// handle github-oauth
|
||||||
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
||||||
if ($input->getOption('unset')) {
|
if ($input->getOption('unset')) {
|
||||||
|
@ -450,6 +472,7 @@ EOT
|
||||||
protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
|
protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
|
||||||
{
|
{
|
||||||
$origK = $k;
|
$origK = $k;
|
||||||
|
$io = $this->getIO();
|
||||||
foreach ($contents as $key => $value) {
|
foreach ($contents as $key => $value) {
|
||||||
if ($k === null && !in_array($key, array('config', 'repositories'))) {
|
if ($k === null && !in_array($key, array('config', 'repositories'))) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -460,13 +483,7 @@ EOT
|
||||||
if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
|
if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
|
||||||
$k .= preg_replace('{^config\.}', '', $key . '.');
|
$k .= preg_replace('{^config\.}', '', $key . '.');
|
||||||
$this->listConfiguration($value, $rawVal, $output, $k);
|
$this->listConfiguration($value, $rawVal, $output, $k);
|
||||||
|
$k = $origK;
|
||||||
if (substr_count($k, '.') > 1) {
|
|
||||||
$k = str_split($k, strrpos($k, '.', -2));
|
|
||||||
$k = $k[0] . '.';
|
|
||||||
} else {
|
|
||||||
$k = $origK;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -484,9 +501,9 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_string($rawVal) && $rawVal != $value) {
|
if (is_string($rawVal) && $rawVal != $value) {
|
||||||
$this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
|
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
|
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,18 +100,17 @@ EOT
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$config = Factory::createConfig();
|
$config = Factory::createConfig();
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
$preferSource = false;
|
$this->updatePreferredOptions($config, $input, $preferSource, $preferDist, true);
|
||||||
$preferDist = false;
|
|
||||||
$this->updatePreferredOptions($config, $input, $preferSource, $preferDist);
|
|
||||||
|
|
||||||
if ($input->getOption('no-custom-installers')) {
|
if ($input->getOption('no-custom-installers')) {
|
||||||
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||||
$input->setOption('no-plugins', true);
|
$input->setOption('no-plugins', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->installProject(
|
return $this->installProject(
|
||||||
$this->getIO(),
|
$io,
|
||||||
$config,
|
$config,
|
||||||
$input->getArgument('package'),
|
$input->getArgument('package'),
|
||||||
$input->getArgument('directory'),
|
$input->getArgument('directory'),
|
||||||
|
@ -145,6 +144,8 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
$composer = Factory::create($io, null, $disablePlugins);
|
$composer = Factory::create($io, null, $disablePlugins);
|
||||||
|
$composer->getDownloadManager()->setOutputProgress(!$noProgress);
|
||||||
|
|
||||||
$fs = new Filesystem();
|
$fs = new Filesystem();
|
||||||
|
|
||||||
if ($noScripts === false) {
|
if ($noScripts === false) {
|
||||||
|
@ -275,9 +276,12 @@ EOT
|
||||||
$pool = new Pool($stability);
|
$pool = new Pool($stability);
|
||||||
$pool->addRepository($sourceRepo);
|
$pool->addRepository($sourceRepo);
|
||||||
|
|
||||||
|
// using those 3 constants to build a version without the 'extra' bit that can contain garbage
|
||||||
|
$phpVersion = PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION;
|
||||||
|
|
||||||
// find the latest version if there are multiple
|
// find the latest version if there are multiple
|
||||||
$versionSelector = new VersionSelector($pool);
|
$versionSelector = new VersionSelector($pool);
|
||||||
$package = $versionSelector->findBestCandidate($name, $packageVersion);
|
$package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion);
|
||||||
|
|
||||||
if (!$package) {
|
if (!$package) {
|
||||||
throw new \InvalidArgumentException("Could not find package $name" . ($packageVersion ? " with version $packageVersion." : " with stability $stability."));
|
throw new \InvalidArgumentException("Could not find package $name" . ($packageVersion ? " with version $packageVersion." : " with stability $stability."));
|
||||||
|
@ -288,7 +292,17 @@ EOT
|
||||||
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
|
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->writeError('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
|
// handler Ctrl+C for unix-like systems
|
||||||
|
if (function_exists('pcntl_signal')) {
|
||||||
|
declare (ticks = 100);
|
||||||
|
pcntl_signal(SIGINT, function () use ($directory) {
|
||||||
|
$fs = new Filesystem();
|
||||||
|
$fs->removeDirectory($directory);
|
||||||
|
exit(130);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->writeError('<info>Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')</info>');
|
||||||
|
|
||||||
if ($disablePlugins) {
|
if ($disablePlugins) {
|
||||||
$io->writeError('<info>Plugins have been disabled.</info>');
|
$io->writeError('<info>Plugins have been disabled.</info>');
|
||||||
|
@ -307,14 +321,15 @@ EOT
|
||||||
$im = $this->createInstallationManager();
|
$im = $this->createInstallationManager();
|
||||||
$im->addInstaller($projectInstaller);
|
$im->addInstaller($projectInstaller);
|
||||||
$im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
|
$im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
|
||||||
$im->notifyInstalls();
|
$im->notifyInstalls($io);
|
||||||
|
|
||||||
$installedFromVcs = 'source' === $package->getInstallationSource();
|
$installedFromVcs = 'source' === $package->getInstallationSource();
|
||||||
|
|
||||||
$io->writeError('<info>Created project in ' . $directory . '</info>');
|
$io->writeError('<info>Created project in ' . $directory . '</info>');
|
||||||
chdir($directory);
|
chdir($directory);
|
||||||
|
|
||||||
putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion());
|
$_SERVER['COMPOSER_ROOT_VERSION'] = $package->getPrettyVersion();
|
||||||
|
putenv('COMPOSER_ROOT_VERSION='.$_SERVER['COMPOSER_ROOT_VERSION']);
|
||||||
|
|
||||||
return $installedFromVcs;
|
return $installedFromVcs;
|
||||||
}
|
}
|
||||||
|
@ -335,18 +350,19 @@ EOT
|
||||||
* Updated preferSource or preferDist based on the preferredInstall config option
|
* Updated preferSource or preferDist based on the preferredInstall config option
|
||||||
* @param Config $config
|
* @param Config $config
|
||||||
* @param InputInterface $input
|
* @param InputInterface $input
|
||||||
* @param boolean $preferSource
|
* @param bool $preferSource
|
||||||
* @param boolean $preferDist
|
* @param bool $preferDist
|
||||||
*/
|
*/
|
||||||
protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist)
|
protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist, $keepVcsRequiresPreferSource = false)
|
||||||
{
|
{
|
||||||
|
$preferSource = false;
|
||||||
|
$preferDist = false;
|
||||||
|
|
||||||
switch ($config->get('preferred-install')) {
|
switch ($config->get('preferred-install')) {
|
||||||
case 'source':
|
case 'source':
|
||||||
$preferSource = true;
|
$preferSource = true;
|
||||||
$preferDist = false;
|
|
||||||
break;
|
break;
|
||||||
case 'dist':
|
case 'dist':
|
||||||
$preferSource = false;
|
|
||||||
$preferDist = true;
|
$preferDist = true;
|
||||||
break;
|
break;
|
||||||
case 'auto':
|
case 'auto':
|
||||||
|
@ -355,8 +371,8 @@ EOT
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) {
|
if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->getOption('keep-vcs'))) {
|
||||||
$preferSource = $input->getOption('prefer-source');
|
$preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->getOption('keep-vcs'));
|
||||||
$preferDist = $input->getOption('prefer-dist');
|
$preferDist = $input->getOption('prefer-dist');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ EOT
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
$outputPackages = array();
|
$outputPackages = array();
|
||||||
|
$io = $this->getIO();
|
||||||
foreach ($repo->getPackages() as $package) {
|
foreach ($repo->getPackages() as $package) {
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
|
foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
|
||||||
|
@ -96,9 +97,9 @@ EOT
|
||||||
|
|
||||||
if ($messages) {
|
if ($messages) {
|
||||||
sort($messages);
|
sort($messages);
|
||||||
$this->getIO()->write($messages);
|
$io->write($messages);
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<info>There is no installed package depending on "'.$needle.'".</info>');
|
$io->writeError('<info>There is no installed package depending on "'.$needle.'".</info>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,15 +51,19 @@ EOT
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
|
||||||
$this->getIO()->write('Checking composer.json: ', false);
|
$io->write('Checking composer.json: ', false);
|
||||||
$this->outputResult($this->checkComposerSchema());
|
$this->outputResult($this->checkComposerSchema());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,57 +73,66 @@ EOT
|
||||||
$config = Factory::createConfig();
|
$config = Factory::createConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->rfs = new RemoteFilesystem($this->getIO(), $config);
|
$this->rfs = new RemoteFilesystem($io, $config);
|
||||||
$this->process = new ProcessExecutor($this->getIO());
|
$this->process = new ProcessExecutor($io);
|
||||||
|
|
||||||
$this->getIO()->write('Checking platform settings: ', false);
|
$io->write('Checking platform settings: ', false);
|
||||||
$this->outputResult($this->checkPlatform());
|
$this->outputResult($this->checkPlatform());
|
||||||
|
|
||||||
$this->getIO()->write('Checking git settings: ', false);
|
$io->write('Checking git settings: ', false);
|
||||||
$this->outputResult($this->checkGit());
|
$this->outputResult($this->checkGit());
|
||||||
|
|
||||||
$this->getIO()->write('Checking http connectivity: ', false);
|
$io->write('Checking http connectivity to packagist: ', false);
|
||||||
$this->outputResult($this->checkHttp());
|
$this->outputResult($this->checkHttp('http'));
|
||||||
|
|
||||||
|
$io->write('Checking https connectivity to packagist: ', false);
|
||||||
|
$this->outputResult($this->checkHttp('https'));
|
||||||
|
|
||||||
$opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
|
$opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
|
||||||
if (!empty($opts['http']['proxy'])) {
|
if (!empty($opts['http']['proxy'])) {
|
||||||
$this->getIO()->write('Checking HTTP proxy: ', false);
|
$io->write('Checking HTTP proxy: ', false);
|
||||||
$this->outputResult($this->checkHttpProxy());
|
$this->outputResult($this->checkHttpProxy());
|
||||||
$this->getIO()->write('Checking HTTP proxy support for request_fulluri: ', false);
|
$io->write('Checking HTTP proxy support for request_fulluri: ', false);
|
||||||
$this->outputResult($this->checkHttpProxyFullUriRequestParam());
|
$this->outputResult($this->checkHttpProxyFullUriRequestParam());
|
||||||
$this->getIO()->write('Checking HTTPS proxy support for request_fulluri: ', false);
|
$io->write('Checking HTTPS proxy support for request_fulluri: ', false);
|
||||||
$this->outputResult($this->checkHttpsProxyFullUriRequestParam());
|
$this->outputResult($this->checkHttpsProxyFullUriRequestParam());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($oauth = $config->get('github-oauth')) {
|
if ($oauth = $config->get('github-oauth')) {
|
||||||
foreach ($oauth as $domain => $token) {
|
foreach ($oauth as $domain => $token) {
|
||||||
$this->getIO()->write('Checking '.$domain.' oauth access: ', false);
|
$io->write('Checking '.$domain.' oauth access: ', false);
|
||||||
$this->outputResult($this->checkGithubOauth($domain, $token));
|
$this->outputResult($this->checkGithubOauth($domain, $token));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->write('Checking github.com rate limit: ', false);
|
$io->write('Checking github.com rate limit: ', false);
|
||||||
$rate = $this->getGithubRateLimit('github.com');
|
try {
|
||||||
|
$rate = $this->getGithubRateLimit('github.com');
|
||||||
if (10 > $rate['remaining']) {
|
$this->outputResult(true);
|
||||||
$this->getIO()->write('<warning>WARNING</warning>');
|
if (10 > $rate['remaining']) {
|
||||||
$this->getIO()->write(sprintf(
|
$io->write('<warning>WARNING</warning>');
|
||||||
'<comment>Github has a rate limit on their API. '
|
$io->write(sprintf(
|
||||||
. 'You currently have <options=bold>%u</options=bold> '
|
'<comment>Github has a rate limit on their API. '
|
||||||
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
|
. 'You currently have <options=bold>%u</options=bold> '
|
||||||
. 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL
|
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
|
||||||
. ' https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens</comment>',
|
. 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL
|
||||||
$rate['remaining'],
|
. ' https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens</comment>',
|
||||||
$rate['limit']
|
$rate['remaining'],
|
||||||
));
|
$rate['limit']
|
||||||
} else {
|
));
|
||||||
$this->getIO()->write('<info>OK</info>');
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($e instanceof TransportException && $e->getCode() === 401) {
|
||||||
|
$this->outputResult('<comment>The oauth token for github.com seems invalid, run "composer config --global --unset github-oauth.github.com" to remove it</comment>');
|
||||||
|
} else {
|
||||||
|
$this->outputResult($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getIO()->write('Checking disk free space: ', false);
|
$io->write('Checking disk free space: ', false);
|
||||||
$this->outputResult($this->checkDiskSpace($config));
|
$this->outputResult($this->checkDiskSpace($config));
|
||||||
|
|
||||||
$this->getIO()->write('Checking composer version: ', false);
|
$io->write('Checking composer version: ', false);
|
||||||
$this->outputResult($this->checkVersion());
|
$this->outputResult($this->checkVersion());
|
||||||
|
|
||||||
return $this->failures;
|
return $this->failures;
|
||||||
|
@ -159,11 +172,10 @@ EOT
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkHttp()
|
private function checkHttp($proto)
|
||||||
{
|
{
|
||||||
$protocol = extension_loaded('openssl') ? 'https' : 'http';
|
|
||||||
try {
|
try {
|
||||||
$this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false);
|
$this->rfs->getContents('packagist.org', $proto . '://packagist.org/packages.json', false);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
@ -252,7 +264,7 @@ EOT
|
||||||
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos';
|
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos';
|
||||||
|
|
||||||
return $this->rfs->getContents($domain, $url, false, array(
|
return $this->rfs->getContents($domain, $url, false, array(
|
||||||
'retry-auth-failure' => false
|
'retry-auth-failure' => false,
|
||||||
)) ? true : 'Unexpected error';
|
)) ? true : 'Unexpected error';
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($e instanceof TransportException && $e->getCode() === 401) {
|
if ($e instanceof TransportException && $e->getCode() === 401) {
|
||||||
|
@ -263,30 +275,28 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $domain
|
||||||
|
* @param string $token
|
||||||
|
* @throws TransportException
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
private function getGithubRateLimit($domain, $token = null)
|
private function getGithubRateLimit($domain, $token = null)
|
||||||
{
|
{
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
|
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
|
||||||
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
|
$json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false));
|
||||||
$json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false));
|
$data = json_decode($json, true);
|
||||||
$data = json_decode($json, true);
|
|
||||||
|
|
||||||
return $data['resources']['core'];
|
return $data['resources']['core'];
|
||||||
} catch (\Exception $e) {
|
|
||||||
if ($e instanceof TransportException && $e->getCode() === 401) {
|
|
||||||
return '<comment>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</comment>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkDiskSpace($config)
|
private function checkDiskSpace($config)
|
||||||
{
|
{
|
||||||
$minSpaceFree = 1024*1024;
|
$minSpaceFree = 1024 * 1024;
|
||||||
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|
||||||
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|
||||||
) {
|
) {
|
||||||
|
@ -308,17 +318,21 @@ EOT
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool|string|\Exception $result
|
||||||
|
*/
|
||||||
private function outputResult($result)
|
private function outputResult($result)
|
||||||
{
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
if (true === $result) {
|
if (true === $result) {
|
||||||
$this->getIO()->write('<info>OK</info>');
|
$io->write('<info>OK</info>');
|
||||||
} else {
|
} else {
|
||||||
$this->failures++;
|
$this->failures++;
|
||||||
$this->getIO()->write('<error>FAIL</error>');
|
$io->write('<error>FAIL</error>');
|
||||||
if ($result instanceof \Exception) {
|
if ($result instanceof \Exception) {
|
||||||
$this->getIO()->write('['.get_class($result).'] '.$result->getMessage());
|
$io->write('['.get_class($result).'] '.$result->getMessage());
|
||||||
} elseif ($result) {
|
} elseif ($result) {
|
||||||
$this->getIO()->write(trim($result));
|
$io->write(trim($result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,10 +373,6 @@ EOT
|
||||||
$errors['hash'] = true;
|
$errors['hash'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extension_loaded('ctype')) {
|
|
||||||
$errors['ctype'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ini_get('allow_url_fopen')) {
|
if (!ini_get('allow_url_fopen')) {
|
||||||
$errors['allow_url_fopen'] = true;
|
$errors['allow_url_fopen'] = true;
|
||||||
}
|
}
|
||||||
|
@ -371,11 +381,11 @@ EOT
|
||||||
$errors['ioncube'] = ioncube_loader_version();
|
$errors['ioncube'] = ioncube_loader_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
|
if (PHP_VERSION_ID < 50302) {
|
||||||
$errors['php'] = PHP_VERSION;
|
$errors['php'] = PHP_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($errors['php']) && version_compare(PHP_VERSION, '5.3.4', '<')) {
|
if (!isset($errors['php']) && PHP_VERSION_ID < 50304) {
|
||||||
$warnings['php'] = PHP_VERSION;
|
$warnings['php'] = PHP_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,11 +441,6 @@ EOT
|
||||||
$text .= "Install it or recompile php without --disable-hash";
|
$text .= "Install it or recompile php without --disable-hash";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ctype':
|
|
||||||
$text = PHP_EOL."The ctype extension is missing.".PHP_EOL;
|
|
||||||
$text .= "Install it or recompile php without --disable-ctype";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unicode':
|
case 'unicode':
|
||||||
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
|
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
|
||||||
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
|
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
|
||||||
|
|
|
@ -31,6 +31,7 @@ class DumpAutoloadCommand extends Command
|
||||||
->setDescription('Dumps the autoloader')
|
->setDescription('Dumps the autoloader')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'),
|
new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'),
|
||||||
|
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'),
|
||||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
|
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
|
@ -52,9 +53,10 @@ EOT
|
||||||
$package = $composer->getPackage();
|
$package = $composer->getPackage();
|
||||||
$config = $composer->getConfig();
|
$config = $composer->getConfig();
|
||||||
|
|
||||||
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
|
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
|
||||||
|
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
|
||||||
|
|
||||||
if ($optimize) {
|
if ($optimize || $authoritative) {
|
||||||
$this->getIO()->writeError('<info>Generating optimized autoload files</info>');
|
$this->getIO()->writeError('<info>Generating optimized autoload files</info>');
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<info>Generating autoload files</info>');
|
$this->getIO()->writeError('<info>Generating autoload files</info>');
|
||||||
|
@ -62,6 +64,7 @@ EOT
|
||||||
|
|
||||||
$generator = $composer->getAutoloadGenerator();
|
$generator = $composer->getAutoloadGenerator();
|
||||||
$generator->setDevMode(!$input->getOption('no-dev'));
|
$generator->setDevMode(!$input->getOption('no-dev'));
|
||||||
|
$generator->setClassMapAuthoritative($authoritative);
|
||||||
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?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\Command\Helper;
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper;
|
|
||||||
|
|
||||||
class DialogHelper extends BaseDialogHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Build text for asking a question. For example:
|
|
||||||
*
|
|
||||||
* "Do you want to continue [yes]:"
|
|
||||||
*
|
|
||||||
* @param string $question The question you want to ask
|
|
||||||
* @param mixed $default Default value to add to message, if false no default will be shown
|
|
||||||
* @param string $sep Separation char for between message and user input
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getQuestion($question, $default = null, $sep = ':')
|
|
||||||
{
|
|
||||||
return $default !== null ?
|
|
||||||
sprintf('<info>%s</info> [<comment>%s</comment>]%s ', $question, $default, $sep) :
|
|
||||||
sprintf('<info>%s</info>%s ', $question, $sep);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,11 +12,10 @@
|
||||||
|
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
use Composer\DependencyResolver\Pool;
|
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\Package\CompletePackageInterface;
|
use Composer\Package\CompletePackageInterface;
|
||||||
use Composer\Repository\CompositeRepository;
|
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
|
use Composer\Repository\ArrayRepository;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -58,70 +57,55 @@ EOT
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$repos = $this->initializeRepos();
|
$repos = $this->initializeRepos();
|
||||||
|
$io = $this->getIO();
|
||||||
$return = 0;
|
$return = 0;
|
||||||
|
|
||||||
foreach ($input->getArgument('packages') as $packageName) {
|
foreach ($input->getArgument('packages') as $packageName) {
|
||||||
|
$handled = false;
|
||||||
|
$packageExists = false;
|
||||||
foreach ($repos as $repo) {
|
foreach ($repos as $repo) {
|
||||||
$package = $this->getPackage($repo, $packageName);
|
foreach ($repo->findPackages($packageName) as $package) {
|
||||||
if ($package instanceof CompletePackageInterface) {
|
$packageExists = true;
|
||||||
break;
|
if ($this->handlePackage($package, $input->getOption('homepage'), $input->getOption('show'))) {
|
||||||
|
$handled = true;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$package = $this->getPackage($repo, $packageName);
|
|
||||||
|
|
||||||
if (!$package instanceof CompletePackageInterface) {
|
if (!$packageExists) {
|
||||||
$return = 1;
|
$return = 1;
|
||||||
$this->getIO()->writeError('<warning>Package '.$packageName.' not found</warning>');
|
$io->writeError('<warning>Package '.$packageName.' not found</warning>');
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$support = $package->getSupport();
|
if (!$handled) {
|
||||||
$url = isset($support['source']) ? $support['source'] : $package->getSourceUrl();
|
|
||||||
if (!$url || $input->getOption('homepage')) {
|
|
||||||
$url = $package->getHomepage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
|
||||||
$return = 1;
|
$return = 1;
|
||||||
$this->getIO()->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
|
$io->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($input->getOption('show')) {
|
|
||||||
$this->getIO()->write(sprintf('<info>%s</info>', $url));
|
|
||||||
} else {
|
|
||||||
$this->openBrowser($url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function handlePackage(CompletePackageInterface $package, $showHomepage, $showOnly)
|
||||||
* finds a package by name
|
|
||||||
*
|
|
||||||
* @param RepositoryInterface $repos
|
|
||||||
* @param string $name
|
|
||||||
* @return CompletePackageInterface
|
|
||||||
*/
|
|
||||||
protected function getPackage(RepositoryInterface $repos, $name)
|
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$support = $package->getSupport();
|
||||||
$pool = new Pool('dev');
|
$url = isset($support['source']) ? $support['source'] : $package->getSourceUrl();
|
||||||
$pool->addRepository($repos);
|
if (!$url || $showHomepage) {
|
||||||
$matches = $pool->whatProvides($name);
|
$url = $package->getHomepage();
|
||||||
|
|
||||||
foreach ($matches as $index => $package) {
|
|
||||||
// skip providers/replacers
|
|
||||||
if ($package->getName() !== $name) {
|
|
||||||
unset($matches[$index]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $package;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($showOnly) {
|
||||||
|
$this->getIO()->write(sprintf('<info>%s</info>', $url));
|
||||||
|
} else {
|
||||||
|
$this->openBrowser($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,14 +145,15 @@ EOT
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
return array(
|
return array_merge(
|
||||||
$composer->getRepositoryManager()->getLocalRepository(),
|
array(new ArrayRepository(array($composer->getPackage()))), // root package
|
||||||
new CompositeRepository($composer->getRepositoryManager()->getRepositories())
|
array($composer->getRepositoryManager()->getLocalRepository()), // installed packages
|
||||||
|
$composer->getRepositoryManager()->getRepositories() // remotes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
||||||
|
|
||||||
return array(new CompositeRepository($defaultRepos));
|
return $defaultRepos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,10 @@ use Composer\DependencyResolver\Pool;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Package\Version\VersionSelector;
|
use Composer\Package\Version\VersionSelector;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Package\Version\VersionParser;
|
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -33,28 +33,18 @@ use Symfony\Component\Process\ExecutableFinder;
|
||||||
*/
|
*/
|
||||||
class InitCommand extends Command
|
class InitCommand extends Command
|
||||||
{
|
{
|
||||||
|
/** @var CompositeRepository */
|
||||||
protected $repos;
|
protected $repos;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
private $gitConfig;
|
private $gitConfig;
|
||||||
|
|
||||||
|
/** @var Pool */
|
||||||
private $pool;
|
private $pool;
|
||||||
|
|
||||||
public function parseAuthorString($author)
|
/**
|
||||||
{
|
* {@inheritdoc}
|
||||||
if (preg_match('/^(?P<name>[- \.,\p{L}\'’]+) <(?P<email>.+?)>$/u', $author, $match)) {
|
*/
|
||||||
if ($this->isValidEmail($match['email'])) {
|
|
||||||
return array(
|
|
||||||
'name' => trim($match['name']),
|
|
||||||
'email' => $match['email']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \InvalidArgumentException(
|
|
||||||
'Invalid author string. Must be in the format: '.
|
|
||||||
'John Smith <john@example.com>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
|
@ -65,6 +55,7 @@ class InitCommand extends Command
|
||||||
new InputOption('description', null, InputOption::VALUE_REQUIRED, 'Description of package'),
|
new InputOption('description', null, InputOption::VALUE_REQUIRED, 'Description of package'),
|
||||||
new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'),
|
new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'),
|
||||||
// new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'),
|
// new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'),
|
||||||
|
new InputOption('type', null, InputOption::VALUE_OPTIONAL, 'Type of package'),
|
||||||
new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'),
|
new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'),
|
||||||
new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
|
new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
|
||||||
new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
|
new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
|
||||||
|
@ -82,11 +73,12 @@ EOT
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$dialog = $this->getHelperSet()->get('dialog');
|
$whitelist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license');
|
||||||
|
|
||||||
$whitelist = array('name', 'description', 'author', 'homepage', 'require', 'require-dev', 'stability', 'license');
|
|
||||||
|
|
||||||
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
|
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
|
||||||
|
|
||||||
|
@ -113,17 +105,13 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = new JsonFile('composer.json');
|
$file = new JsonFile('composer.json');
|
||||||
|
|
||||||
$json = $file->encode($options);
|
$json = $file->encode($options);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
if ($input->isInteractive()) {
|
if ($input->isInteractive()) {
|
||||||
$this->getIO()->writeError(array(
|
$io->writeError(array('', $json, ''));
|
||||||
'',
|
if (!$io->askConfirmation('Do you confirm generation [<comment>yes</comment>]? ', true)) {
|
||||||
$json,
|
$io->writeError('<error>Command aborted</error>');
|
||||||
''
|
|
||||||
));
|
|
||||||
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
|
|
||||||
$this->getIO()->writeError('<error>Command aborted</error>');
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -139,29 +127,32 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->hasVendorIgnore($ignoreFile)) {
|
if (!$this->hasVendorIgnore($ignoreFile)) {
|
||||||
$question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]?';
|
$question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]? ';
|
||||||
|
|
||||||
if ($dialog->askConfirmation($output, $question, true)) {
|
if ($io->askConfirmation($question, true)) {
|
||||||
$this->addVendorIgnore($ignoreFile);
|
$this->addVendorIgnore($ignoreFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
protected function interact(InputInterface $input, OutputInterface $output)
|
protected function interact(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$git = $this->getGitConfig();
|
$git = $this->getGitConfig();
|
||||||
|
$io = $this->getIO();
|
||||||
$dialog = $this->getHelperSet()->get('dialog');
|
|
||||||
$formatter = $this->getHelperSet()->get('formatter');
|
$formatter = $this->getHelperSet()->get('formatter');
|
||||||
$this->getIO()->writeError(array(
|
|
||||||
|
$io->writeError(array(
|
||||||
'',
|
'',
|
||||||
$formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
|
$formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
|
||||||
''
|
'',
|
||||||
));
|
));
|
||||||
|
|
||||||
// namespace
|
// namespace
|
||||||
$this->getIO()->writeError(array(
|
$io->writeError(array(
|
||||||
'',
|
'',
|
||||||
'This command will guide you through creating your composer.json config.',
|
'This command will guide you through creating your composer.json config.',
|
||||||
'',
|
'',
|
||||||
|
@ -191,9 +182,8 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = $dialog->askAndValidate(
|
$name = $io->askAndValidate(
|
||||||
$output,
|
'Package name (<vendor>/<name>) [<comment>'.$name.'</comment>]: ',
|
||||||
$dialog->getQuestion('Package name (<vendor>/<name>)', $name),
|
|
||||||
function ($value) use ($name) {
|
function ($value) use ($name) {
|
||||||
if (null === $value) {
|
if (null === $value) {
|
||||||
return $name;
|
return $name;
|
||||||
|
@ -206,14 +196,15 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
},
|
||||||
|
null,
|
||||||
|
$name
|
||||||
);
|
);
|
||||||
$input->setOption('name', $name);
|
$input->setOption('name', $name);
|
||||||
|
|
||||||
$description = $input->getOption('description') ?: false;
|
$description = $input->getOption('description') ?: false;
|
||||||
$description = $dialog->ask(
|
$description = $io->ask(
|
||||||
$output,
|
'Description [<comment>'.$description.'</comment>]: ',
|
||||||
$dialog->getQuestion('Description', $description),
|
|
||||||
$description
|
$description
|
||||||
);
|
);
|
||||||
$input->setOption('description', $description);
|
$input->setOption('description', $description);
|
||||||
|
@ -225,22 +216,22 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
$self = $this;
|
$self = $this;
|
||||||
$author = $dialog->askAndValidate(
|
$author = $io->askAndValidate(
|
||||||
$output,
|
'Author [<comment>'.$author.'</comment>]: ',
|
||||||
$dialog->getQuestion('Author', $author),
|
|
||||||
function ($value) use ($self, $author) {
|
function ($value) use ($self, $author) {
|
||||||
$value = $value ?: $author;
|
$value = $value ?: $author;
|
||||||
$author = $self->parseAuthorString($value);
|
$author = $self->parseAuthorString($value);
|
||||||
|
|
||||||
return sprintf('%s <%s>', $author['name'], $author['email']);
|
return sprintf('%s <%s>', $author['name'], $author['email']);
|
||||||
}
|
},
|
||||||
|
null,
|
||||||
|
$author
|
||||||
);
|
);
|
||||||
$input->setOption('author', $author);
|
$input->setOption('author', $author);
|
||||||
|
|
||||||
$minimumStability = $input->getOption('stability') ?: '';
|
$minimumStability = $input->getOption('stability') ?: null;
|
||||||
$minimumStability = $dialog->askAndValidate(
|
$minimumStability = $io->askAndValidate(
|
||||||
$output,
|
'Minimum Stability [<comment>'.$minimumStability.'</comment>]: ',
|
||||||
$dialog->getQuestion('Minimum Stability', $minimumStability),
|
|
||||||
function ($value) use ($self, $minimumStability) {
|
function ($value) use ($self, $minimumStability) {
|
||||||
if (null === $value) {
|
if (null === $value) {
|
||||||
return $minimumStability;
|
return $minimumStability;
|
||||||
|
@ -254,36 +245,65 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
},
|
||||||
|
null,
|
||||||
|
$minimumStability
|
||||||
);
|
);
|
||||||
$input->setOption('stability', $minimumStability);
|
$input->setOption('stability', $minimumStability);
|
||||||
|
|
||||||
|
$type = $input->getOption('type') ?: false;
|
||||||
|
$type = $io->ask(
|
||||||
|
'Package Type [<comment>'.$type.'</comment>]: ',
|
||||||
|
$type
|
||||||
|
);
|
||||||
|
$input->setOption('type', $type);
|
||||||
|
|
||||||
$license = $input->getOption('license') ?: false;
|
$license = $input->getOption('license') ?: false;
|
||||||
$license = $dialog->ask(
|
$license = $io->ask(
|
||||||
$output,
|
'License [<comment>'.$license.'</comment>]: ',
|
||||||
$dialog->getQuestion('License', $license),
|
|
||||||
$license
|
$license
|
||||||
);
|
);
|
||||||
$input->setOption('license', $license);
|
$input->setOption('license', $license);
|
||||||
|
|
||||||
$this->getIO()->writeError(array(
|
$io->writeError(array('', 'Define your dependencies.', ''));
|
||||||
'',
|
|
||||||
'Define your dependencies.',
|
|
||||||
''
|
|
||||||
));
|
|
||||||
|
|
||||||
|
$question = 'Would you like to define your dependencies (require) interactively [<comment>yes</comment>]? ';
|
||||||
$requirements = array();
|
$requirements = array();
|
||||||
if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dependencies (require) interactively', 'yes', '?'), true)) {
|
if ($io->askConfirmation($question, true)) {
|
||||||
$requirements = $this->determineRequirements($input, $output, $input->getOption('require'));
|
$requirements = $this->determineRequirements($input, $output, $input->getOption('require'));
|
||||||
}
|
}
|
||||||
$input->setOption('require', $requirements);
|
$input->setOption('require', $requirements);
|
||||||
|
|
||||||
|
$question = 'Would you like to define your dev dependencies (require-dev) interactively [<comment>yes</comment>]? ';
|
||||||
$devRequirements = array();
|
$devRequirements = array();
|
||||||
if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dev dependencies (require-dev) interactively', 'yes', '?'), true)) {
|
if ($io->askConfirmation($question, true)) {
|
||||||
$devRequirements = $this->determineRequirements($input, $output, $input->getOption('require-dev'));
|
$devRequirements = $this->determineRequirements($input, $output, $input->getOption('require-dev'));
|
||||||
}
|
}
|
||||||
$input->setOption('require-dev', $devRequirements);
|
$input->setOption('require-dev', $devRequirements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param string $author
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function parseAuthorString($author)
|
||||||
|
{
|
||||||
|
if (preg_match('/^(?P<name>[- \.,\p{L}\p{N}\'’]+) <(?P<email>.+?)>$/u', $author, $match)) {
|
||||||
|
if ($this->isValidEmail($match['email'])) {
|
||||||
|
return array(
|
||||||
|
'name' => trim($match['name']),
|
||||||
|
'email' => $match['email'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid author string. Must be in the format: '.
|
||||||
|
'John Smith <john@example.com>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected function findPackages($name)
|
protected function findPackages($name)
|
||||||
{
|
{
|
||||||
return $this->getRepos()->search($name);
|
return $this->getRepos()->search($name);
|
||||||
|
@ -301,22 +321,20 @@ EOT
|
||||||
return $this->repos;
|
return $this->repos;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array())
|
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null)
|
||||||
{
|
{
|
||||||
$dialog = $this->getHelperSet()->get('dialog');
|
|
||||||
$prompt = $dialog->getQuestion('Search for a package', false, ':');
|
|
||||||
|
|
||||||
if ($requires) {
|
if ($requires) {
|
||||||
$requires = $this->normalizeRequirements($requires);
|
$requires = $this->normalizeRequirements($requires);
|
||||||
$result = array();
|
$result = array();
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
foreach ($requires as $requirement) {
|
foreach ($requires as $requirement) {
|
||||||
if (!isset($requirement['version'])) {
|
if (!isset($requirement['version'])) {
|
||||||
// determine the best version automatically
|
// determine the best version automatically
|
||||||
$version = $this->findBestVersionForPackage($input, $requirement['name']);
|
$version = $this->findBestVersionForPackage($input, $requirement['name'], $phpVersion);
|
||||||
$requirement['version'] = $version;
|
$requirement['version'] = $version;
|
||||||
|
|
||||||
$this->getIO()->writeError(sprintf(
|
$io->writeError(sprintf(
|
||||||
'Using version <info>%s</info> for <info>%s</info>',
|
'Using version <info>%s</info> for <info>%s</info>',
|
||||||
$requirement['version'],
|
$requirement['version'],
|
||||||
$requirement['name']
|
$requirement['name']
|
||||||
|
@ -329,7 +347,9 @@ EOT
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (null !== $package = $dialog->ask($output, $prompt)) {
|
$versionParser = new VersionParser();
|
||||||
|
$io = $this->getIO();
|
||||||
|
while (null !== $package = $io->ask('Search for a package: ')) {
|
||||||
$matches = $this->findPackages($package);
|
$matches = $this->findPackages($package);
|
||||||
|
|
||||||
if (count($matches)) {
|
if (count($matches)) {
|
||||||
|
@ -345,34 +365,49 @@ EOT
|
||||||
|
|
||||||
// no match, prompt which to pick
|
// no match, prompt which to pick
|
||||||
if (!$exactMatch) {
|
if (!$exactMatch) {
|
||||||
$this->getIO()->writeError(array(
|
$io->writeError(array(
|
||||||
'',
|
'',
|
||||||
sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
|
sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
|
||||||
''
|
'',
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->getIO()->writeError($choices);
|
$io->writeError($choices);
|
||||||
$this->getIO()->writeError('');
|
$io->writeError('');
|
||||||
|
|
||||||
$validator = function ($selection) use ($matches) {
|
$validator = function ($selection) use ($matches, $versionParser) {
|
||||||
if ('' === $selection) {
|
if ('' === $selection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_numeric($selection) && preg_match('{^\s*(\S+)\s+(\S.*)\s*$}', $selection, $matches)) {
|
if (is_numeric($selection) && isset($matches[(int) $selection])) {
|
||||||
return $matches[1].' '.$matches[2];
|
$package = $matches[(int) $selection];
|
||||||
|
|
||||||
|
return $package['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($matches[(int) $selection])) {
|
if (preg_match('{^\s*(?P<name>[\S/]+)(?:\s+(?P<version>\S+))?\s*$}', $selection, $packageMatches)) {
|
||||||
throw new \Exception('Not a valid selection');
|
if (isset($packageMatches['version'])) {
|
||||||
|
// parsing `acme/example ~2.3`
|
||||||
|
|
||||||
|
// validate version constraint
|
||||||
|
$versionParser->parseConstraints($packageMatches['version']);
|
||||||
|
|
||||||
|
return $packageMatches['name'].' '.$packageMatches['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsing `acme/example`
|
||||||
|
return $packageMatches['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$package = $matches[(int) $selection];
|
throw new \Exception('Not a valid selection');
|
||||||
|
|
||||||
return $package['name'];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add, or the complete package name if it is not listed', false, ':'), $validator, 3);
|
$package = $io->askAndValidate(
|
||||||
|
'Enter package # to add, or the complete package name if it is not listed: ',
|
||||||
|
$validator,
|
||||||
|
3,
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no constraint yet, determine the best version automatically
|
// no constraint yet, determine the best version automatically
|
||||||
|
@ -383,16 +418,17 @@ EOT
|
||||||
return $input ?: false;
|
return $input ?: false;
|
||||||
};
|
};
|
||||||
|
|
||||||
$constraint = $dialog->askAndValidate(
|
$constraint = $io->askAndValidate(
|
||||||
$output,
|
'Enter the version constraint to require (or leave blank to use the latest version): ',
|
||||||
$dialog->getQuestion('Enter the version constraint to require (or leave blank to use the latest version)', false, ':'),
|
|
||||||
$validator,
|
$validator,
|
||||||
3)
|
3,
|
||||||
;
|
false
|
||||||
if (false === $constraint) {
|
);
|
||||||
$constraint = $this->findBestVersionForPackage($input, $package);
|
|
||||||
|
|
||||||
$this->getIO()->writeError(sprintf(
|
if (false === $constraint) {
|
||||||
|
$constraint = $this->findBestVersionForPackage($input, $package, $phpVersion);
|
||||||
|
|
||||||
|
$io->writeError(sprintf(
|
||||||
'Using version <info>%s</info> for <info>%s</info>',
|
'Using version <info>%s</info> for <info>%s</info>',
|
||||||
$constraint,
|
$constraint,
|
||||||
$package
|
$package
|
||||||
|
@ -515,7 +551,7 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
// php <5.3.3 has a very broken email validator, so bypass checks
|
// php <5.3.3 has a very broken email validator, so bypass checks
|
||||||
if (version_compare(PHP_VERSION, '5.3.3', '<')) {
|
if (PHP_VERSION_ID < 50303) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,14 +591,15 @@ EOT
|
||||||
*
|
*
|
||||||
* @param InputInterface $input
|
* @param InputInterface $input
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return string
|
* @param string $phpVersion
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function findBestVersionForPackage(InputInterface $input, $name)
|
private function findBestVersionForPackage(InputInterface $input, $name, $phpVersion)
|
||||||
{
|
{
|
||||||
// find the latest version allowed in this pool
|
// find the latest version allowed in this pool
|
||||||
$versionSelector = new VersionSelector($this->getPool($input));
|
$versionSelector = new VersionSelector($this->getPool($input));
|
||||||
$package = $versionSelector->findBestCandidate($name);
|
$package = $versionSelector->findBestCandidate($name, null, $phpVersion);
|
||||||
|
|
||||||
if (!$package) {
|
if (!$package) {
|
||||||
throw new \InvalidArgumentException(sprintf(
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
|
|
@ -46,6 +46,7 @@ class InstallCommand extends Command
|
||||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
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('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('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
|
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
|
||||||
))
|
))
|
||||||
|
@ -64,24 +65,24 @@ EOT
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
if ($args = $input->getArgument('packages')) {
|
if ($args = $input->getArgument('packages')) {
|
||||||
$this->getIO()->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
|
$io->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->getOption('no-custom-installers')) {
|
if ($input->getOption('no-custom-installers')) {
|
||||||
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||||
$input->setOption('no-plugins', true);
|
$input->setOption('no-plugins', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->getOption('dev')) {
|
if ($input->getOption('dev')) {
|
||||||
$this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
|
$io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
|
||||||
}
|
}
|
||||||
|
|
||||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||||
$io = $this->getIO();
|
|
||||||
|
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
@ -110,7 +111,8 @@ EOT
|
||||||
$preferDist = $input->getOption('prefer-dist');
|
$preferDist = $input->getOption('prefer-dist');
|
||||||
}
|
}
|
||||||
|
|
||||||
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
|
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
|
||||||
|
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
|
||||||
|
|
||||||
$install
|
$install
|
||||||
->setDryRun($input->getOption('dry-run'))
|
->setDryRun($input->getOption('dry-run'))
|
||||||
|
@ -121,6 +123,7 @@ EOT
|
||||||
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
||||||
->setRunScripts(!$input->getOption('no-scripts'))
|
->setRunScripts(!$input->getOption('no-scripts'))
|
||||||
->setOptimizeAutoloader($optimize)
|
->setOptimizeAutoloader($optimize)
|
||||||
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,11 @@
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\Version\VersionParser;
|
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
use Symfony\Component\Console\Helper\Table;
|
use Symfony\Component\Console\Helper\Table;
|
||||||
use Symfony\Component\Console\Helper\TableStyle;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
@ -57,8 +55,6 @@ EOT
|
||||||
$root = $composer->getPackage();
|
$root = $composer->getPackage();
|
||||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
|
|
||||||
$versionParser = new VersionParser;
|
|
||||||
|
|
||||||
if ($input->getOption('no-dev')) {
|
if ($input->getOption('no-dev')) {
|
||||||
$packages = $this->filterRequiredPackages($repo, $root);
|
$packages = $this->filterRequiredPackages($repo, $root);
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,14 +62,15 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($packages);
|
ksort($packages);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
switch ($format = $input->getOption('format')) {
|
switch ($format = $input->getOption('format')) {
|
||||||
case 'text':
|
case 'text':
|
||||||
$this->getIO()->write('Name: <comment>'.$root->getPrettyName().'</comment>');
|
$io->write('Name: <comment>'.$root->getPrettyName().'</comment>');
|
||||||
$this->getIO()->write('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
|
$io->write('Version: <comment>'.$root->getFullPrettyVersion().'</comment>');
|
||||||
$this->getIO()->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
|
$io->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
|
||||||
$this->getIO()->write('Dependencies:');
|
$io->write('Dependencies:');
|
||||||
$this->getIO()->write('');
|
$io->write('');
|
||||||
|
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
$table->setStyle('compact');
|
$table->setStyle('compact');
|
||||||
|
@ -83,7 +80,7 @@ EOT
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
$table->addRow(array(
|
$table->addRow(array(
|
||||||
$package->getPrettyName(),
|
$package->getPrettyName(),
|
||||||
$versionParser->formatVersion($package),
|
$package->getFullPrettyVersion(),
|
||||||
implode(', ', $package->getLicense()) ?: 'none',
|
implode(', ', $package->getLicense()) ?: 'none',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -93,14 +90,14 @@ EOT
|
||||||
case 'json':
|
case 'json':
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
$dependencies[$package->getPrettyName()] = array(
|
$dependencies[$package->getPrettyName()] = array(
|
||||||
'version' => $versionParser->formatVersion($package),
|
'version' => $package->getFullPrettyVersion(),
|
||||||
'license' => $package->getLicense(),
|
'license' => $package->getLicense(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getIO()->write(JsonFile::encode(array(
|
$io->write(JsonFile::encode(array(
|
||||||
'name' => $root->getPrettyName(),
|
'name' => $root->getPrettyName(),
|
||||||
'version' => $versionParser->formatVersion($root),
|
'version' => $root->getFullPrettyVersion(),
|
||||||
'license' => $root->getLicense(),
|
'license' => $root->getLicense(),
|
||||||
'dependencies' => $dependencies,
|
'dependencies' => $dependencies,
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -42,6 +42,8 @@ class RemoveCommand extends Command
|
||||||
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
|
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.'),
|
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
|
||||||
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore 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`.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The <info>remove</info> command removes a package from the current
|
The <info>remove</info> command removes a package from the current
|
||||||
|
@ -68,20 +70,20 @@ EOT
|
||||||
|
|
||||||
$type = $input->getOption('dev') ? 'require-dev' : 'require';
|
$type = $input->getOption('dev') ? 'require-dev' : 'require';
|
||||||
$altType = !$input->getOption('dev') ? 'require-dev' : 'require';
|
$altType = !$input->getOption('dev') ? 'require-dev' : 'require';
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
if (isset($composer[$type][$package])) {
|
if (isset($composer[$type][$package])) {
|
||||||
$json->removeLink($type, $package);
|
$json->removeLink($type, $package);
|
||||||
} elseif (isset($composer[$altType][$package])) {
|
} elseif (isset($composer[$altType][$package])) {
|
||||||
$this->getIO()->writeError('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
|
$io->writeError('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
|
||||||
$dialog = $this->getHelperSet()->get('dialog');
|
if ($io->isInteractive()) {
|
||||||
if ($this->getIO()->isInteractive()) {
|
if ($io->askConfirmation('Do you want to remove it from '.$altType.' [<comment>yes</comment>]? ', true)) {
|
||||||
if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) {
|
|
||||||
$json->removeLink($altType, $package);
|
$json->removeLink($altType, $package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
|
$io->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +94,6 @@ EOT
|
||||||
// Update packages
|
// Update packages
|
||||||
$composer = $this->getComposer();
|
$composer = $this->getComposer();
|
||||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||||
$io = $this->getIO();
|
|
||||||
|
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
@ -100,9 +101,14 @@ EOT
|
||||||
$install = Installer::create($io, $composer);
|
$install = Installer::create($io, $composer);
|
||||||
|
|
||||||
$updateDevMode = !$input->getOption('update-no-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');
|
||||||
|
|
||||||
$install
|
$install
|
||||||
->setVerbose($input->getOption('verbose'))
|
->setVerbose($input->getOption('verbose'))
|
||||||
->setDevMode($updateDevMode)
|
->setDevMode($updateDevMode)
|
||||||
|
->setOptimizeAutoloader($optimize)
|
||||||
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setUpdate(true)
|
->setUpdate(true)
|
||||||
->setUpdateWhitelist($packages)
|
->setUpdateWhitelist($packages)
|
||||||
->setWhitelistDependencies($input->getOption('update-with-dependencies'))
|
->setWhitelistDependencies($input->getOption('update-with-dependencies'))
|
||||||
|
@ -111,7 +117,7 @@ EOT
|
||||||
|
|
||||||
$status = $install->run();
|
$status = $install->run();
|
||||||
if ($status !== 0) {
|
if ($status !== 0) {
|
||||||
$this->getIO()->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
|
$io->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
|
||||||
file_put_contents($jsonFile->getPath(), $composerBackup);
|
file_put_contents($jsonFile->getPath(), $composerBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use Composer\Factory;
|
||||||
use Composer\Installer;
|
use Composer\Installer;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Json\JsonManipulator;
|
use Composer\Json\JsonManipulator;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Semver\VersionParser;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
|
@ -48,6 +48,8 @@ class RequireCommand extends InitCommand
|
||||||
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
|
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
|
||||||
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||||
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
|
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
|
||||||
|
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`.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The require command adds required packages to your composer.json and installs them.
|
The require command adds required packages to your composer.json and installs them.
|
||||||
|
@ -64,24 +66,29 @@ EOT
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$file = Factory::getComposerFile();
|
$file = Factory::getComposerFile();
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
$newlyCreated = !file_exists($file);
|
$newlyCreated = !file_exists($file);
|
||||||
if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
|
if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
|
||||||
$this->getIO()->writeError('<error>'.$file.' could not be created.</error>');
|
$io->writeError('<error>'.$file.' could not be created.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!is_readable($file)) {
|
if (!is_readable($file)) {
|
||||||
$this->getIO()->writeError('<error>'.$file.' is not readable.</error>');
|
$io->writeError('<error>'.$file.' is not readable.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!is_writable($file)) {
|
if (!is_writable($file)) {
|
||||||
$this->getIO()->writeError('<error>'.$file.' is not writable.</error>');
|
$io->writeError('<error>'.$file.' is not writable.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filesize($file) === 0) {
|
||||||
|
file_put_contents($file, "{\n}\n");
|
||||||
|
}
|
||||||
|
|
||||||
$json = new JsonFile($file);
|
$json = new JsonFile($file);
|
||||||
$composerDefinition = $json->read();
|
$composerDefinition = $json->read();
|
||||||
$composerBackup = file_get_contents($json->getPath());
|
$composerBackup = file_get_contents($json->getPath());
|
||||||
|
@ -89,12 +96,15 @@ EOT
|
||||||
$composer = $this->getComposer();
|
$composer = $this->getComposer();
|
||||||
$repos = $composer->getRepositoryManager()->getRepositories();
|
$repos = $composer->getRepositoryManager()->getRepositories();
|
||||||
|
|
||||||
|
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
|
||||||
|
// initialize $this->repos as it is used by the parent InitCommand
|
||||||
$this->repos = new CompositeRepository(array_merge(
|
$this->repos = new CompositeRepository(array_merge(
|
||||||
array(new PlatformRepository),
|
array(new PlatformRepository(array(), $platformOverrides)),
|
||||||
$repos
|
$repos
|
||||||
));
|
));
|
||||||
|
|
||||||
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'));
|
$phpVersion = $this->repos->findPackage('php', '*')->getVersion();
|
||||||
|
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion);
|
||||||
|
|
||||||
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
|
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
|
||||||
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
|
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
|
||||||
|
@ -122,18 +132,19 @@ EOT
|
||||||
$json->write($composerDefinition);
|
$json->write($composerDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getIO()->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
|
$io->writeError('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
|
||||||
|
|
||||||
if ($input->getOption('no-update')) {
|
if ($input->getOption('no-update')) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$updateDevMode = !$input->getOption('update-no-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');
|
||||||
|
|
||||||
// Update packages
|
// Update packages
|
||||||
$this->resetComposer();
|
$this->resetComposer();
|
||||||
$composer = $this->getComposer();
|
$composer = $this->getComposer();
|
||||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||||
$io = $this->getIO();
|
|
||||||
|
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
@ -145,6 +156,8 @@ EOT
|
||||||
->setPreferSource($input->getOption('prefer-source'))
|
->setPreferSource($input->getOption('prefer-source'))
|
||||||
->setPreferDist($input->getOption('prefer-dist'))
|
->setPreferDist($input->getOption('prefer-dist'))
|
||||||
->setDevMode($updateDevMode)
|
->setDevMode($updateDevMode)
|
||||||
|
->setOptimizeAutoloader($optimize)
|
||||||
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setUpdate(true)
|
->setUpdate(true)
|
||||||
->setUpdateWhitelist(array_keys($requirements))
|
->setUpdateWhitelist(array_keys($requirements))
|
||||||
->setWhitelistDependencies($input->getOption('update-with-dependencies'))
|
->setWhitelistDependencies($input->getOption('update-with-dependencies'))
|
||||||
|
@ -154,10 +167,10 @@ EOT
|
||||||
$status = $install->run();
|
$status = $install->run();
|
||||||
if ($status !== 0) {
|
if ($status !== 0) {
|
||||||
if ($newlyCreated) {
|
if ($newlyCreated) {
|
||||||
$this->getIO()->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
|
$io->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
|
||||||
unlink($json->getPath());
|
unlink($json->getPath());
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
|
$io->writeError("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
|
||||||
file_put_contents($json->getPath(), $composerBackup);
|
file_put_contents($json->getPath(), $composerBackup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,8 @@ EOT
|
||||||
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
||||||
$binDir = $composer->getConfig()->get('bin-dir');
|
$binDir = $composer->getConfig()->get('bin-dir');
|
||||||
if (is_dir($binDir)) {
|
if (is_dir($binDir)) {
|
||||||
putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
|
$_SERVER['PATH'] = realpath($binDir).PATH_SEPARATOR.getenv('PATH');
|
||||||
|
putenv('PATH='.$_SERVER['PATH']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$args = $input->getArgument('args');
|
$args = $input->getArgument('args');
|
||||||
|
@ -103,9 +104,10 @@ EOT
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getIO()->writeError('<info>scripts:</info>');
|
$io = $this->getIO();
|
||||||
|
$io->writeError('<info>scripts:</info>');
|
||||||
foreach ($scripts as $name => $script) {
|
foreach ($scripts as $name => $script) {
|
||||||
$this->getIO()->write(' ' . $name);
|
$io->write(' ' . $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -57,7 +57,8 @@ EOT
|
||||||
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
||||||
$binDir = $composer->getConfig()->get('bin-dir');
|
$binDir = $composer->getConfig()->get('bin-dir');
|
||||||
if (is_dir($binDir)) {
|
if (is_dir($binDir)) {
|
||||||
putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
|
$_SERVER['PATH'] = realpath($binDir).PATH_SEPARATOR.getenv('PATH');
|
||||||
|
putenv('PATH='.$_SERVER['PATH']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$args = $input->getArguments();
|
$args = $input->getArguments();
|
||||||
|
|
|
@ -56,13 +56,14 @@ EOT
|
||||||
{
|
{
|
||||||
// init repos
|
// init repos
|
||||||
$platformRepo = new PlatformRepository;
|
$platformRepo = new PlatformRepository;
|
||||||
|
$io = $this->getIO();
|
||||||
if ($composer = $this->getComposer(false)) {
|
if ($composer = $this->getComposer(false)) {
|
||||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
||||||
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
|
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
|
||||||
} else {
|
} else {
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
$defaultRepos = Factory::createDefaultRepositories($io);
|
||||||
$this->getIO()->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
|
$io->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
|
||||||
$installedRepo = $platformRepo;
|
$installedRepo = $platformRepo;
|
||||||
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ EOT
|
||||||
$results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
|
$results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
|
||||||
|
|
||||||
foreach ($results as $result) {
|
foreach ($results as $result) {
|
||||||
$this->getIO()->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
|
$io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,8 @@ EOT
|
||||||
{
|
{
|
||||||
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
||||||
$config = Factory::createConfig();
|
$config = Factory::createConfig();
|
||||||
$remoteFilesystem = new RemoteFilesystem($this->getIO(), $config);
|
$io = $this->getIO();
|
||||||
|
$remoteFilesystem = new RemoteFilesystem($io, $config);
|
||||||
$cacheDir = $config->get('cache-dir');
|
$cacheDir = $config->get('cache-dir');
|
||||||
$rollbackDir = $config->get('home');
|
$rollbackDir = $config->get('home');
|
||||||
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
|
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
|
||||||
|
@ -84,13 +85,13 @@ EOT
|
||||||
$updateVersion = $input->getArgument('version') ?: $latestVersion;
|
$updateVersion = $input->getArgument('version') ?: $latestVersion;
|
||||||
|
|
||||||
if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
|
if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
|
||||||
$this->getIO()->writeError('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
|
$io->writeError('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Composer::VERSION === $updateVersion) {
|
if (Composer::VERSION === $updateVersion) {
|
||||||
$this->getIO()->writeError('<info>You are already using composer version '.$updateVersion.'.</info>');
|
$io->writeError('<info>You are already using composer version '.$updateVersion.'.</info>');
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -104,11 +105,11 @@ EOT
|
||||||
self::OLD_INSTALL_EXT
|
self::OLD_INSTALL_EXT
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->getIO()->writeError(sprintf("Updating to version <info>%s</info>.", $updateVersion));
|
$io->writeError(sprintf("Updating to version <info>%s</info>.", $updateVersion));
|
||||||
$remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar");
|
$remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar");
|
||||||
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
|
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
|
||||||
if (!file_exists($tempFilename)) {
|
if (!file_exists($tempFilename)) {
|
||||||
$this->getIO()->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
|
$io->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -120,22 +121,22 @@ EOT
|
||||||
$fs = new Filesystem;
|
$fs = new Filesystem;
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
$file = (string) $file;
|
$file = (string) $file;
|
||||||
$this->getIO()->writeError('<info>Removing: '.$file.'</info>');
|
$io->writeError('<info>Removing: '.$file.'</info>');
|
||||||
$fs->remove($file);
|
$fs->remove($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
|
if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
|
||||||
$this->getIO()->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
|
$io->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
|
||||||
$this->getIO()->writeError('<error>Please re-run the self-update command to try again.</error>');
|
$io->writeError('<error>Please re-run the self-update command to try again.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($backupFile)) {
|
if (file_exists($backupFile)) {
|
||||||
$this->getIO()->writeError('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
|
$io->writeError('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
|
$io->writeError('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,9 +161,10 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
|
$oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
|
||||||
$this->getIO()->writeError(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
|
$io = $this->getIO();
|
||||||
|
$io->writeError(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
|
||||||
if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
|
if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
|
||||||
$this->getIO()->writeError('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
|
$io->writeError('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use Composer\DependencyResolver\Pool;
|
||||||
use Composer\DependencyResolver\DefaultPolicy;
|
use Composer\DependencyResolver\DefaultPolicy;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\Package\CompletePackageInterface;
|
use Composer\Package\CompletePackageInterface;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Semver\VersionParser;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
@ -28,6 +28,7 @@ use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Repository\ComposerRepository;
|
use Composer\Repository\ComposerRepository;
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
|
use Composer\Spdx\SpdxLicenses;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||||
|
@ -70,6 +71,7 @@ EOT
|
||||||
$platformRepo = new PlatformRepository;
|
$platformRepo = new PlatformRepository;
|
||||||
|
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
|
$io = $this->getIO();
|
||||||
if ($input->getOption('self')) {
|
if ($input->getOption('self')) {
|
||||||
$package = $this->getComposer()->getPackage();
|
$package = $this->getComposer()->getPackage();
|
||||||
$repos = $installedRepo = new ArrayRepository(array($package));
|
$repos = $installedRepo = new ArrayRepository(array($package));
|
||||||
|
@ -82,17 +84,17 @@ EOT
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
$repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
|
$repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
|
||||||
} else {
|
} else {
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
$defaultRepos = Factory::createDefaultRepositories($io);
|
||||||
$repos = new CompositeRepository($defaultRepos);
|
$repos = new CompositeRepository($defaultRepos);
|
||||||
$this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
|
$io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
|
||||||
}
|
}
|
||||||
} elseif ($composer) {
|
} elseif ($composer) {
|
||||||
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
|
||||||
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
|
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
|
||||||
} else {
|
} else {
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
$defaultRepos = Factory::createDefaultRepositories($io);
|
||||||
$this->getIO()->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
|
$io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
|
||||||
$installedRepo = $platformRepo;
|
$installedRepo = $platformRepo;
|
||||||
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
$repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
|
||||||
}
|
}
|
||||||
|
@ -115,18 +117,18 @@ EOT
|
||||||
$versions = array($package->getPrettyVersion() => $package->getVersion());
|
$versions = array($package->getPrettyVersion() => $package->getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->printMeta($input, $output, $package, $versions, $installedRepo, $repos);
|
$this->printMeta($package, $versions, $installedRepo);
|
||||||
$this->printLinks($input, $output, $package, 'requires');
|
$this->printLinks($package, 'requires');
|
||||||
$this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
|
$this->printLinks($package, 'devRequires', 'requires (dev)');
|
||||||
if ($package->getSuggests()) {
|
if ($package->getSuggests()) {
|
||||||
$this->getIO()->write("\n<info>suggests</info>");
|
$io->write("\n<info>suggests</info>");
|
||||||
foreach ($package->getSuggests() as $suggested => $reason) {
|
foreach ($package->getSuggests() as $suggested => $reason) {
|
||||||
$this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>');
|
$io->write($suggested . ' <comment>' . $reason . '</comment>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->printLinks($input, $output, $package, 'provides');
|
$this->printLinks($package, 'provides');
|
||||||
$this->printLinks($input, $output, $package, 'conflicts');
|
$this->printLinks($package, 'conflicts');
|
||||||
$this->printLinks($input, $output, $package, 'replaces');
|
$this->printLinks($package, 'replaces');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +174,7 @@ EOT
|
||||||
foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
|
foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
|
||||||
if (isset($packages[$type])) {
|
if (isset($packages[$type])) {
|
||||||
if ($tree) {
|
if ($tree) {
|
||||||
$this->getIO()->write($type);
|
$io->write($type);
|
||||||
}
|
}
|
||||||
ksort($packages[$type]);
|
ksort($packages[$type]);
|
||||||
|
|
||||||
|
@ -180,7 +182,7 @@ EOT
|
||||||
foreach ($packages[$type] as $package) {
|
foreach ($packages[$type] as $package) {
|
||||||
if (is_object($package)) {
|
if (is_object($package)) {
|
||||||
$nameLength = max($nameLength, strlen($package->getPrettyName()));
|
$nameLength = max($nameLength, strlen($package->getPrettyName()));
|
||||||
$versionLength = max($versionLength, strlen($this->versionParser->formatVersion($package)));
|
$versionLength = max($versionLength, strlen($package->getFullPrettyVersion()));
|
||||||
} else {
|
} else {
|
||||||
$nameLength = max($nameLength, $package);
|
$nameLength = max($nameLength, $package);
|
||||||
}
|
}
|
||||||
|
@ -195,6 +197,11 @@ EOT
|
||||||
$width--;
|
$width--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($input->getOption('path') && null === $composer) {
|
||||||
|
$io->writeError('No composer.json found in the current directory, disabling "path" option');
|
||||||
|
$input->setOption('path', false);
|
||||||
|
}
|
||||||
|
|
||||||
$writePath = !$input->getOption('name-only') && $input->getOption('path');
|
$writePath = !$input->getOption('name-only') && $input->getOption('path');
|
||||||
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion && ($nameLength + $versionLength + 3 <= $width);
|
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion && ($nameLength + $versionLength + 3 <= $width);
|
||||||
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path') && ($nameLength + ($showVersion ? $versionLength : 0) + 24 <= $width);
|
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path') && ($nameLength + ($showVersion ? $versionLength : 0) + 24 <= $width);
|
||||||
|
@ -203,7 +210,7 @@ EOT
|
||||||
$output->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
|
$output->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
|
||||||
|
|
||||||
if ($writeVersion) {
|
if ($writeVersion) {
|
||||||
$output->write(' ' . str_pad($this->versionParser->formatVersion($package), $versionLength, ' '), false);
|
$output->write(' ' . str_pad($package->getFullPrettyVersion(), $versionLength, ' '), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($writeDescription) {
|
if ($writeDescription) {
|
||||||
|
@ -222,10 +229,10 @@ EOT
|
||||||
} else {
|
} else {
|
||||||
$output->write($indent . $package);
|
$output->write($indent . $package);
|
||||||
}
|
}
|
||||||
$this->getIO()->write('');
|
$io->write('');
|
||||||
}
|
}
|
||||||
if ($tree) {
|
if ($tree) {
|
||||||
$this->getIO()->write('');
|
$io->write('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,8 +245,8 @@ EOT
|
||||||
* @param RepositoryInterface $repos
|
* @param RepositoryInterface $repos
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $version
|
* @param string $version
|
||||||
* @return array array(CompletePackageInterface, array of versions)
|
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
* @return array array(CompletePackageInterface, array of versions)
|
||||||
*/
|
*/
|
||||||
protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null)
|
protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null)
|
||||||
{
|
{
|
||||||
|
@ -272,9 +279,9 @@ EOT
|
||||||
$matches[$index] = $package->getId();
|
$matches[$index] = $package->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// select prefered package according to policy rules
|
// select preferred package according to policy rules
|
||||||
if (!$matchedPackage && $matches && $prefered = $policy->selectPreferedPackages($pool, array(), $matches)) {
|
if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) {
|
||||||
$matchedPackage = $pool->literalToPackage($prefered[0]);
|
$matchedPackage = $pool->literalToPackage($preferred[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array($matchedPackage, $versions);
|
return array($matchedPackage, $versions);
|
||||||
|
@ -283,55 +290,56 @@ EOT
|
||||||
/**
|
/**
|
||||||
* prints package meta data
|
* prints package meta data
|
||||||
*/
|
*/
|
||||||
protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
|
protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
|
||||||
{
|
{
|
||||||
$this->getIO()->write('<info>name</info> : ' . $package->getPrettyName());
|
$io = $this->getIO();
|
||||||
$this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
|
$io->write('<info>name</info> : ' . $package->getPrettyName());
|
||||||
$this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
|
$io->write('<info>descrip.</info> : ' . $package->getDescription());
|
||||||
$this->printVersions($input, $output, $package, $versions, $installedRepo, $repos);
|
$io->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
|
||||||
$this->getIO()->write('<info>type</info> : ' . $package->getType());
|
$this->printVersions($package, $versions, $installedRepo);
|
||||||
$this->getIO()->write('<info>license</info> : ' . implode(', ', $package->getLicense()));
|
$io->write('<info>type</info> : ' . $package->getType());
|
||||||
$this->getIO()->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
|
$this->printLicenses($package);
|
||||||
$this->getIO()->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
|
$io->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
|
||||||
$this->getIO()->write('<info>names</info> : ' . implode(', ', $package->getNames()));
|
$io->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
|
||||||
|
$io->write('<info>names</info> : ' . implode(', ', $package->getNames()));
|
||||||
|
|
||||||
if ($package->isAbandoned()) {
|
if ($package->isAbandoned()) {
|
||||||
$replacement = ($package->getReplacementPackage() !== null)
|
$replacement = ($package->getReplacementPackage() !== null)
|
||||||
? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
|
? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
$this->getIO()->writeError(
|
$io->writeError(
|
||||||
sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement)
|
sprintf('<warning>Attention: This package is abandoned and no longer maintained.%s</warning>', $replacement)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($package->getSupport()) {
|
if ($package->getSupport()) {
|
||||||
$this->getIO()->write("\n<info>support</info>");
|
$io->write("\n<info>support</info>");
|
||||||
foreach ($package->getSupport() as $type => $value) {
|
foreach ($package->getSupport() as $type => $value) {
|
||||||
$this->getIO()->write('<comment>' . $type . '</comment> : '.$value);
|
$io->write('<comment>' . $type . '</comment> : '.$value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($package->getAutoload()) {
|
if ($package->getAutoload()) {
|
||||||
$this->getIO()->write("\n<info>autoload</info>");
|
$io->write("\n<info>autoload</info>");
|
||||||
foreach ($package->getAutoload() as $type => $autoloads) {
|
foreach ($package->getAutoload() as $type => $autoloads) {
|
||||||
$this->getIO()->write('<comment>' . $type . '</comment>');
|
$io->write('<comment>' . $type . '</comment>');
|
||||||
|
|
||||||
if ($type === 'psr-0') {
|
if ($type === 'psr-0') {
|
||||||
foreach ($autoloads as $name => $path) {
|
foreach ($autoloads as $name => $path) {
|
||||||
$this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
$io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
||||||
}
|
}
|
||||||
} elseif ($type === 'psr-4') {
|
} elseif ($type === 'psr-4') {
|
||||||
foreach ($autoloads as $name => $path) {
|
foreach ($autoloads as $name => $path) {
|
||||||
$this->getIO()->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
$io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
||||||
}
|
}
|
||||||
} elseif ($type === 'classmap') {
|
} elseif ($type === 'classmap') {
|
||||||
$this->getIO()->write(implode(', ', $autoloads));
|
$io->write(implode(', ', $autoloads));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($package->getIncludePaths()) {
|
if ($package->getIncludePaths()) {
|
||||||
$this->getIO()->write('<comment>include-path</comment>');
|
$io->write('<comment>include-path</comment>');
|
||||||
$this->getIO()->write(implode(', ', $package->getIncludePaths()));
|
$io->write(implode(', ', $package->getIncludePaths()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,7 +347,7 @@ EOT
|
||||||
/**
|
/**
|
||||||
* prints all available versions of this package and highlights the installed one if any
|
* prints all available versions of this package and highlights the installed one if any
|
||||||
*/
|
*/
|
||||||
protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
|
protected function printVersions(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
|
||||||
{
|
{
|
||||||
uasort($versions, 'version_compare');
|
uasort($versions, 'version_compare');
|
||||||
$versions = array_keys(array_reverse($versions));
|
$versions = array_keys(array_reverse($versions));
|
||||||
|
@ -361,21 +369,50 @@ EOT
|
||||||
/**
|
/**
|
||||||
* print link objects
|
* print link objects
|
||||||
*
|
*
|
||||||
* @param InputInterface $input
|
|
||||||
* @param OutputInterface $output
|
|
||||||
* @param CompletePackageInterface $package
|
* @param CompletePackageInterface $package
|
||||||
* @param string $linkType
|
* @param string $linkType
|
||||||
* @param string $title
|
* @param string $title
|
||||||
*/
|
*/
|
||||||
protected function printLinks(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, $linkType, $title = null)
|
protected function printLinks(CompletePackageInterface $package, $linkType, $title = null)
|
||||||
{
|
{
|
||||||
$title = $title ?: $linkType;
|
$title = $title ?: $linkType;
|
||||||
|
$io = $this->getIO();
|
||||||
if ($links = $package->{'get'.ucfirst($linkType)}()) {
|
if ($links = $package->{'get'.ucfirst($linkType)}()) {
|
||||||
$this->getIO()->write("\n<info>" . $title . "</info>");
|
$io->write("\n<info>" . $title . "</info>");
|
||||||
|
|
||||||
foreach ($links as $link) {
|
foreach ($links as $link) {
|
||||||
$this->getIO()->write($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
|
$io->write($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the licenses of a package with metadata
|
||||||
|
*
|
||||||
|
* @param CompletePackageInterface $package
|
||||||
|
*/
|
||||||
|
protected function printLicenses(CompletePackageInterface $package)
|
||||||
|
{
|
||||||
|
$spdxLicenses = new SpdxLicenses();
|
||||||
|
|
||||||
|
$licenses = $package->getLicense();
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
|
foreach ($licenses as $licenseId) {
|
||||||
|
$license = $spdxLicenses->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
|
||||||
|
|
||||||
|
if (!$license) {
|
||||||
|
$out = $licenseId;
|
||||||
|
} else {
|
||||||
|
// is license OSI approved?
|
||||||
|
if ($license[1] === true) {
|
||||||
|
$out = sprintf('%s (%s) (OSI approved) %s', $license[0], $licenseId, $license[2]);
|
||||||
|
} else {
|
||||||
|
$out = sprintf('%s (%s) %s', $license[0], $licenseId, $license[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->write('<info>license</info> : ' . $out);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ EOT
|
||||||
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
|
||||||
|
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
// list packages
|
// list packages
|
||||||
foreach ($installedRepo->getPackages() as $package) {
|
foreach ($installedRepo->getPackages() as $package) {
|
||||||
|
@ -68,6 +69,10 @@ EOT
|
||||||
if ($downloader instanceof ChangeReportInterface) {
|
if ($downloader instanceof ChangeReportInterface) {
|
||||||
$targetDir = $im->getInstallPath($package);
|
$targetDir = $im->getInstallPath($package);
|
||||||
|
|
||||||
|
if (is_link($targetDir)) {
|
||||||
|
$errors[$targetDir] = $targetDir . ' is a symbolic link.';
|
||||||
|
}
|
||||||
|
|
||||||
if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
|
if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
|
||||||
$errors[$targetDir] = $changes;
|
$errors[$targetDir] = $changes;
|
||||||
}
|
}
|
||||||
|
@ -76,9 +81,9 @@ EOT
|
||||||
|
|
||||||
// output errors/warnings
|
// output errors/warnings
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$this->getIO()->writeError('<info>No local changes</info>');
|
$io->writeError('<info>No local changes</info>');
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<error>You have changes in the following dependencies:</error>');
|
$io->writeError('<error>You have changes in the following dependencies:</error>');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($errors as $path => $changes) {
|
foreach ($errors as $path => $changes) {
|
||||||
|
@ -86,15 +91,15 @@ EOT
|
||||||
$indentedChanges = implode("\n", array_map(function ($line) {
|
$indentedChanges = implode("\n", array_map(function ($line) {
|
||||||
return ' ' . ltrim($line);
|
return ' ' . ltrim($line);
|
||||||
}, explode("\n", $changes)));
|
}, explode("\n", $changes)));
|
||||||
$this->getIO()->write('<info>'.$path.'</info>:');
|
$io->write('<info>'.$path.'</info>:');
|
||||||
$this->getIO()->write($indentedChanges);
|
$io->write($indentedChanges);
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->write($path);
|
$io->write($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($errors && !$input->getOption('verbose')) {
|
if ($errors && !$input->getOption('verbose')) {
|
||||||
$this->getIO()->writeError('Use --verbose (-v) to see modified files');
|
$io->writeError('Use --verbose (-v) to see modified files');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch post-status-command
|
// Dispatch post-status-command
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?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\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class SuggestsCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('suggests')
|
||||||
|
->setDescription('Show package suggestions')
|
||||||
|
->setDefinition(array(
|
||||||
|
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
|
||||||
|
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
|
||||||
|
))
|
||||||
|
->setHelp(<<<EOT
|
||||||
|
|
||||||
|
The <info>%command.name%</info> command shows suggested packages.
|
||||||
|
|
||||||
|
With <info>-v</info> you also see which package suggested it and why.
|
||||||
|
|
||||||
|
EOT
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$lock = $this->getComposer()->getLocker()->getLockData();
|
||||||
|
|
||||||
|
if (empty($lock)) {
|
||||||
|
throw new \RuntimeException('Lockfile seems to be empty?');
|
||||||
|
}
|
||||||
|
|
||||||
|
$packages = $lock['packages'];
|
||||||
|
|
||||||
|
if (!$input->getOption('no-dev')) {
|
||||||
|
$packages += $lock['packages-dev'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter = $input->getArgument('packages');
|
||||||
|
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
if (empty($package['suggest'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($filter) && !in_array($package['name'], $filter)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->printSuggestions($packages, $package['name'], $package['suggest']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function printSuggestions($installed, $source, $suggestions)
|
||||||
|
{
|
||||||
|
foreach ($suggestions as $suggestion => $reason) {
|
||||||
|
foreach ($installed as $package) {
|
||||||
|
if ($package['name'] === $suggestion) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($reason)) {
|
||||||
|
$reason = '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->printSuggestion($source, $suggestion, $reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function printSuggestion($package, $suggestion, $reason)
|
||||||
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
|
if ($io->isVerbose()) {
|
||||||
|
$io->write(sprintf('<comment>%s</comment> suggests <info>%s</info>: %s', $package, $suggestion, $reason));
|
||||||
|
} else {
|
||||||
|
$io->write(sprintf('<info>%s</info>', $suggestion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ class UpdateCommand extends Command
|
||||||
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
|
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
|
||||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
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('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('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||||
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
|
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('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
|
||||||
|
@ -74,18 +75,18 @@ EOT
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
if ($input->getOption('no-custom-installers')) {
|
if ($input->getOption('no-custom-installers')) {
|
||||||
$this->getIO()->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
|
||||||
$input->setOption('no-plugins', true);
|
$input->setOption('no-plugins', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->getOption('dev')) {
|
if ($input->getOption('dev')) {
|
||||||
$this->getIO()->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
|
$io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
|
||||||
}
|
}
|
||||||
|
|
||||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||||
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||||
$io = $this->getIO();
|
|
||||||
|
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
@ -114,7 +115,8 @@ EOT
|
||||||
$preferDist = $input->getOption('prefer-dist');
|
$preferDist = $input->getOption('prefer-dist');
|
||||||
}
|
}
|
||||||
|
|
||||||
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
|
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
|
||||||
|
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
|
||||||
|
|
||||||
$install
|
$install
|
||||||
->setDryRun($input->getOption('dry-run'))
|
->setDryRun($input->getOption('dry-run'))
|
||||||
|
@ -125,6 +127,7 @@ EOT
|
||||||
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
||||||
->setRunScripts(!$input->getOption('no-scripts'))
|
->setRunScripts(!$input->getOption('no-scripts'))
|
||||||
->setOptimizeAutoloader($optimize)
|
->setOptimizeAutoloader($optimize)
|
||||||
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setUpdate(true)
|
->setUpdate(true)
|
||||||
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
|
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
|
||||||
->setWhitelistDependencies($input->getOption('with-dependencies'))
|
->setWhitelistDependencies($input->getOption('with-dependencies'))
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
|
use Composer\Factory;
|
||||||
use Composer\Package\Loader\ValidatingArrayLoader;
|
use Composer\Package\Loader\ValidatingArrayLoader;
|
||||||
use Composer\Util\ConfigValidator;
|
use Composer\Util\ConfigValidator;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
@ -34,14 +35,22 @@ class ValidateCommand extends Command
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->setName('validate')
|
->setName('validate')
|
||||||
->setDescription('Validates a composer.json')
|
->setDescription('Validates a composer.json and composer.lock')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
|
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
|
||||||
|
new InputOption('no-check-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-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
|
||||||
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
|
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', './composer.json'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The validate command validates a given composer.json
|
The validate command validates a given composer.json and composer.lock
|
||||||
|
|
||||||
|
Exit codes in case of errors are:
|
||||||
|
1 validation warning(s), only when --strict is given
|
||||||
|
2 validation error(s)
|
||||||
|
3 file unreadable or missing
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
);
|
);
|
||||||
|
@ -56,35 +65,86 @@ EOT
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$file = $input->getArgument('file');
|
$file = $input->getArgument('file');
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
if (!file_exists($file)) {
|
if (!file_exists($file)) {
|
||||||
$this->getIO()->writeError('<error>' . $file . ' not found.</error>');
|
$io->writeError('<error>' . $file . ' not found.</error>');
|
||||||
|
|
||||||
return 1;
|
return 3;
|
||||||
}
|
}
|
||||||
if (!is_readable($file)) {
|
if (!is_readable($file)) {
|
||||||
$this->getIO()->writeError('<error>' . $file . ' is not readable.</error>');
|
$io->writeError('<error>' . $file . ' is not readable.</error>');
|
||||||
|
|
||||||
return 1;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = new ConfigValidator($this->getIO());
|
$validator = new ConfigValidator($io);
|
||||||
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
|
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
|
||||||
$checkPublish = !$input->getOption('no-check-publish');
|
$checkPublish = !$input->getOption('no-check-publish');
|
||||||
|
$checkLock = !$input->getOption('no-check-lock');
|
||||||
|
$isStrict = $input->getOption('strict');
|
||||||
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
|
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
|
||||||
|
|
||||||
// output errors/warnings
|
$lockErrors = array();
|
||||||
|
$composer = Factory::create($io, $file);
|
||||||
|
$locker = $composer->getLocker();
|
||||||
|
if ($locker->isLocked() && !$locker->isFresh()) {
|
||||||
|
$lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true);
|
||||||
|
|
||||||
|
$exitCode = $errors || ($publishErrors && $checkPublish) || ($lockErrors && $checkLock) ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||||
|
|
||||||
|
if ($input->getOption('with-dependencies')) {
|
||||||
|
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
|
foreach ($localRepo->getPackages() as $package) {
|
||||||
|
$path = $composer->getInstallationManager()->getInstallPath($package);
|
||||||
|
$file = $path . '/composer.json';
|
||||||
|
if (is_dir($path) && file_exists($file)) {
|
||||||
|
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
|
||||||
|
$this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors);
|
||||||
|
|
||||||
|
$depCode = $errors || ($publishErrors && $checkPublish) ? 2 : ($isStrict && $warnings ? 1 : 0);
|
||||||
|
$exitCode = max($depCode, $exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false)
|
||||||
|
{
|
||||||
if (!$errors && !$publishErrors && !$warnings) {
|
if (!$errors && !$publishErrors && !$warnings) {
|
||||||
$this->getIO()->write('<info>' . $file . ' is valid</info>');
|
$io->write('<info>' . $name . ' is valid</info>');
|
||||||
} elseif (!$errors && !$publishErrors) {
|
} elseif (!$errors && !$publishErrors) {
|
||||||
$this->getIO()->writeError('<info>' . $file . ' is valid, but with a few warnings</info>');
|
$io->writeError('<info>' . $name . ' is valid, but with a few warnings</info>');
|
||||||
$this->getIO()->writeError('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
|
if ($printSchemaUrl) {
|
||||||
|
$io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
|
||||||
|
}
|
||||||
} elseif (!$errors) {
|
} elseif (!$errors) {
|
||||||
$this->getIO()->writeError('<info>' . $file . ' is valid for simple usage with composer but has</info>');
|
$io->writeError('<info>' . $name . ' is valid for simple usage with composer but has</info>');
|
||||||
$this->getIO()->writeError('<info>strict errors that make it unable to be published as a package:</info>');
|
$io->writeError('<info>strict errors that make it unable to be published as a package:</info>');
|
||||||
$this->getIO()->writeError('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
|
if ($printSchemaUrl) {
|
||||||
|
$io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->writeError('<error>' . $file . ' is invalid, the following errors/warnings were found:</error>');
|
$io->writeError('<error>' . $name . ' is invalid, the following errors/warnings were found:</error>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If checking publish errors, display them as errors, otherwise just show them as warnings
|
||||||
|
if ($checkPublish) {
|
||||||
|
$errors = array_merge($errors, $publishErrors);
|
||||||
|
} else {
|
||||||
|
$warnings = array_merge($warnings, $publishErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If checking lock errors, display them as errors, otherwise just show them as warnings
|
||||||
|
if ($checkLock) {
|
||||||
|
$errors = array_merge($errors, $lockErrors);
|
||||||
|
} else {
|
||||||
|
$warnings = array_merge($warnings, $lockErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
$messages = array(
|
$messages = array(
|
||||||
|
@ -92,19 +152,10 @@ EOT
|
||||||
'warning' => $warnings,
|
'warning' => $warnings,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If checking publish errors, display them errors, otherwise just show them as warnings
|
|
||||||
if ($checkPublish) {
|
|
||||||
$messages['error'] = array_merge($messages['error'], $publishErrors);
|
|
||||||
} else {
|
|
||||||
$messages['warning'] = array_merge($messages['warning'], $publishErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($messages as $style => $msgs) {
|
foreach ($messages as $style => $msgs) {
|
||||||
foreach ($msgs as $msg) {
|
foreach ($msgs as $msg) {
|
||||||
$this->getIO()->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
|
$io->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors || ($publishErrors && $checkPublish) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,10 @@
|
||||||
namespace Composer;
|
namespace Composer;
|
||||||
|
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
|
use Composer\Spdx\SpdxLicenses;
|
||||||
use Symfony\Component\Finder\Finder;
|
use Symfony\Component\Finder\Finder;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
|
use Seld\PharUtils\Timestamps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Compiler class compiles composer into a phar
|
* The Compiler class compiles composer into a phar
|
||||||
|
@ -31,8 +33,8 @@ class Compiler
|
||||||
/**
|
/**
|
||||||
* Compiles composer into a single phar file
|
* Compiles composer into a single phar file
|
||||||
*
|
*
|
||||||
* @throws \RuntimeException
|
|
||||||
* @param string $pharFile The full path to the file to create
|
* @param string $pharFile The full path to the file to create
|
||||||
|
* @throws \RuntimeException
|
||||||
*/
|
*/
|
||||||
public function compile($pharFile = 'composer.phar')
|
public function compile($pharFile = 'composer.phar')
|
||||||
{
|
{
|
||||||
|
@ -51,9 +53,8 @@ class Compiler
|
||||||
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.');
|
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$date = new \DateTime(trim($process->getOutput()));
|
$this->versionDate = new \DateTime(trim($process->getOutput()));
|
||||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
$this->versionDate->setTimezone(new \DateTimeZone('UTC'));
|
||||||
$this->versionDate = $date->format('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
$process = new Process('git describe --tags --exact-match HEAD');
|
$process = new Process('git describe --tags --exact-match HEAD');
|
||||||
if ($process->run() == 0) {
|
if ($process->run() == 0) {
|
||||||
|
@ -73,6 +74,10 @@ class Compiler
|
||||||
|
|
||||||
$phar->startBuffering();
|
$phar->startBuffering();
|
||||||
|
|
||||||
|
$finderSort = function ($a, $b) {
|
||||||
|
return strcmp(strtr($a->getRealPath(), '\\', '/'), strtr($b->getRealPath(), '\\', '/'));
|
||||||
|
};
|
||||||
|
|
||||||
$finder = new Finder();
|
$finder = new Finder();
|
||||||
$finder->files()
|
$finder->files()
|
||||||
->ignoreVCS(true)
|
->ignoreVCS(true)
|
||||||
|
@ -80,6 +85,7 @@ class Compiler
|
||||||
->notName('Compiler.php')
|
->notName('Compiler.php')
|
||||||
->notName('ClassLoader.php')
|
->notName('ClassLoader.php')
|
||||||
->in(__DIR__.'/..')
|
->in(__DIR__.'/..')
|
||||||
|
->sort($finderSort)
|
||||||
;
|
;
|
||||||
|
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
|
@ -90,13 +96,15 @@ class Compiler
|
||||||
$finder = new Finder();
|
$finder = new Finder();
|
||||||
$finder->files()
|
$finder->files()
|
||||||
->name('*.json')
|
->name('*.json')
|
||||||
->in(__DIR__ . '/../../res')
|
->in(__DIR__.'/../../res')
|
||||||
|
->in(SpdxLicenses::getResourcesDir())
|
||||||
|
->sort($finderSort)
|
||||||
;
|
;
|
||||||
|
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
$this->addFile($phar, $file, false);
|
$this->addFile($phar, $file, false);
|
||||||
}
|
}
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../src/Composer/IO/hiddeninput.exe'), false);
|
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/seld/cli-prompt/res/hiddeninput.exe'), false);
|
||||||
|
|
||||||
$finder = new Finder();
|
$finder = new Finder();
|
||||||
$finder->files()
|
$finder->files()
|
||||||
|
@ -108,7 +116,11 @@ class Compiler
|
||||||
->exclude('docs')
|
->exclude('docs')
|
||||||
->in(__DIR__.'/../../vendor/symfony/')
|
->in(__DIR__.'/../../vendor/symfony/')
|
||||||
->in(__DIR__.'/../../vendor/seld/jsonlint/')
|
->in(__DIR__.'/../../vendor/seld/jsonlint/')
|
||||||
|
->in(__DIR__.'/../../vendor/seld/cli-prompt/')
|
||||||
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
|
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
|
||||||
|
->in(__DIR__.'/../../vendor/composer/spdx-licenses/')
|
||||||
|
->in(__DIR__.'/../../vendor/composer/semver/')
|
||||||
|
->sort($finderSort)
|
||||||
;
|
;
|
||||||
|
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
|
@ -137,6 +149,11 @@ class Compiler
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../LICENSE'), false);
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../LICENSE'), false);
|
||||||
|
|
||||||
unset($phar);
|
unset($phar);
|
||||||
|
|
||||||
|
// re-sign the phar with reproducible timestamp / signature
|
||||||
|
$util = new Timestamps($pharFile);
|
||||||
|
$util->updateTimestamps($this->versionDate);
|
||||||
|
$util->save($pharFile, \Phar::SHA1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addFile($phar, $file, $strip = true)
|
private function addFile($phar, $file, $strip = true)
|
||||||
|
@ -153,7 +170,7 @@ class Compiler
|
||||||
if ($path === 'src/Composer/Composer.php') {
|
if ($path === 'src/Composer/Composer.php') {
|
||||||
$content = str_replace('@package_version@', $this->version, $content);
|
$content = str_replace('@package_version@', $this->version, $content);
|
||||||
$content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content);
|
$content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content);
|
||||||
$content = str_replace('@release_date@', $this->versionDate, $content);
|
$content = str_replace('@release_date@', $this->versionDate->format('Y-m-d H:i:s'), $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
$phar->addFromString($path, $content);
|
$phar->addFromString($path, $content);
|
||||||
|
@ -229,9 +246,9 @@ Phar::mapPhar('composer.phar');
|
||||||
|
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
// add warning once the phar is older than 30 days
|
// add warning once the phar is older than 60 days
|
||||||
if (preg_match('{^[a-f0-9]+$}', $this->version)) {
|
if (preg_match('{^[a-f0-9]+$}', $this->version)) {
|
||||||
$warningTime = time() + 30*86400;
|
$warningTime = $this->versionDate->format('U') + 60 * 86400;
|
||||||
$stub .= "define('COMPOSER_DEV_WARNING_TIME', $warningTime);\n";
|
$stub .= "define('COMPOSER_DEV_WARNING_TIME', $warningTime);\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Config
|
||||||
'cache-ttl' => 15552000, // 6 months
|
'cache-ttl' => 15552000, // 6 months
|
||||||
'cache-files-ttl' => null, // fallback to cache-ttl
|
'cache-files-ttl' => null, // fallback to cache-ttl
|
||||||
'cache-files-maxsize' => '300MiB',
|
'cache-files-maxsize' => '300MiB',
|
||||||
|
'bin-compat' => 'auto',
|
||||||
'discard-changes' => false,
|
'discard-changes' => false,
|
||||||
'autoloader-suffix' => null,
|
'autoloader-suffix' => null,
|
||||||
'optimize-autoloader' => false,
|
'optimize-autoloader' => false,
|
||||||
|
@ -45,6 +46,9 @@ class Config
|
||||||
'github-expose-hostname' => true,
|
'github-expose-hostname' => true,
|
||||||
'gitlab-domains' => array('gitlab.com'),
|
'gitlab-domains' => array('gitlab.com'),
|
||||||
'store-auths' => 'prompt',
|
'store-auths' => 'prompt',
|
||||||
|
'platform' => array(),
|
||||||
|
'archive-format' => 'tar',
|
||||||
|
'archive-dir' => '.',
|
||||||
// valid keys without defaults (auth config stuff):
|
// valid keys without defaults (auth config stuff):
|
||||||
// github-oauth
|
// github-oauth
|
||||||
// http-basic
|
// http-basic
|
||||||
|
@ -55,7 +59,7 @@ class Config
|
||||||
'type' => 'composer',
|
'type' => 'composer',
|
||||||
'url' => 'https?://packagist.org',
|
'url' => 'https?://packagist.org',
|
||||||
'allow_ssl_downgrade' => true,
|
'allow_ssl_downgrade' => true,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
private $config;
|
private $config;
|
||||||
|
@ -66,7 +70,7 @@ class Config
|
||||||
private $useEnvironment;
|
private $useEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param boolean $useEnvironment Use COMPOSER_ environment variables to replace config settings
|
* @param bool $useEnvironment Use COMPOSER_ environment variables to replace config settings
|
||||||
*/
|
*/
|
||||||
public function __construct($useEnvironment = true, $baseDir = null)
|
public function __construct($useEnvironment = true, $baseDir = null)
|
||||||
{
|
{
|
||||||
|
@ -178,7 +182,7 @@ class Config
|
||||||
return $val;
|
return $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val);
|
return ($flags & self::RELATIVE_PATHS == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
|
||||||
|
|
||||||
case 'cache-ttl':
|
case 'cache-ttl':
|
||||||
return (int) $this->config[$key];
|
return (int) $this->config[$key];
|
||||||
|
@ -216,6 +220,17 @@ class Config
|
||||||
case 'home':
|
case 'home':
|
||||||
return rtrim($this->process($this->config[$key], $flags), '/\\');
|
return rtrim($this->process($this->config[$key], $flags), '/\\');
|
||||||
|
|
||||||
|
case 'bin-compat':
|
||||||
|
$value = $this->getComposerEnv('COMPOSER_BIN_COMPAT') ?: $this->config[$key];
|
||||||
|
|
||||||
|
if (!in_array($value, array('auto', 'full'))) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
"Invalid value for 'bin-compat': {$value}. Expected auto, full"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
|
||||||
case 'discard-changes':
|
case 'discard-changes':
|
||||||
if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) {
|
if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) {
|
||||||
if (!in_array($env, array('stash', 'true', 'false', '1', '0'), true)) {
|
if (!in_array($env, array('stash', 'true', 'false', '1', '0'), true)) {
|
||||||
|
@ -329,8 +344,8 @@ class Config
|
||||||
* This should be used to read COMPOSER_ environment variables
|
* This should be used to read COMPOSER_ environment variables
|
||||||
* that overload config values.
|
* that overload config values.
|
||||||
*
|
*
|
||||||
* @param string $var
|
* @param string $var
|
||||||
* @return string|boolean
|
* @return string|bool
|
||||||
*/
|
*/
|
||||||
private function getComposerEnv($var)
|
private function getComposerEnv($var)
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,7 +79,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
||||||
public function addConfigSetting($name, $value)
|
public function addConfigSetting($name, $value)
|
||||||
{
|
{
|
||||||
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
|
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
|
||||||
if ($key === 'github-oauth' || $key === 'http-basic') {
|
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
|
||||||
list($key, $host) = explode('.', $key, 2);
|
list($key, $host) = explode('.', $key, 2);
|
||||||
if ($this->authConfig) {
|
if ($this->authConfig) {
|
||||||
$config[$key][$host] = $val;
|
$config[$key][$host] = $val;
|
||||||
|
@ -98,7 +98,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
||||||
public function removeConfigSetting($name)
|
public function removeConfigSetting($name)
|
||||||
{
|
{
|
||||||
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
|
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
|
||||||
if ($key === 'github-oauth' || $key === 'http-basic') {
|
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
|
||||||
list($key, $host) = explode('.', $key, 2);
|
list($key, $host) = explode('.', $key, 2);
|
||||||
if ($this->authConfig) {
|
if ($this->authConfig) {
|
||||||
unset($config[$key][$host]);
|
unset($config[$key][$host]);
|
||||||
|
|
|
@ -20,7 +20,6 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||||
use Composer\Command;
|
use Composer\Command;
|
||||||
use Composer\Command\Helper\DialogHelper;
|
|
||||||
use Composer\Composer;
|
use Composer\Composer;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
@ -66,7 +65,6 @@ class Application extends BaseApplication
|
||||||
date_default_timezone_set(@date_default_timezone_get());
|
date_default_timezone_set(@date_default_timezone_get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorHandler::register();
|
|
||||||
parent::__construct('Composer', Composer::VERSION);
|
parent::__construct('Composer', Composer::VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,9 +88,11 @@ class Application extends BaseApplication
|
||||||
public function doRun(InputInterface $input, OutputInterface $output)
|
public function doRun(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$this->io = new ConsoleIO($input, $output, $this->getHelperSet());
|
$this->io = new ConsoleIO($input, $output, $this->getHelperSet());
|
||||||
|
ErrorHandler::register($this->io);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
|
if (PHP_VERSION_ID < 50302) {
|
||||||
$this->getIO()->writeError('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
|
$io->writeError('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined('COMPOSER_DEV_WARNING_TIME')) {
|
if (defined('COMPOSER_DEV_WARNING_TIME')) {
|
||||||
|
@ -105,7 +105,7 @@ class Application extends BaseApplication
|
||||||
}
|
}
|
||||||
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
||||||
if (time() > COMPOSER_DEV_WARNING_TIME) {
|
if (time() > COMPOSER_DEV_WARNING_TIME) {
|
||||||
$this->getIO()->writeError(sprintf('<warning>Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
|
$io->writeError(sprintf('<warning>Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,8 @@ class Application extends BaseApplication
|
||||||
if ($newWorkDir = $this->getNewWorkingDir($input)) {
|
if ($newWorkDir = $this->getNewWorkingDir($input)) {
|
||||||
$oldWorkingDir = getcwd();
|
$oldWorkingDir = getcwd();
|
||||||
chdir($newWorkDir);
|
chdir($newWorkDir);
|
||||||
if ($this->getIO()->isDebug() >= 4) {
|
if ($io->isDebug() >= 4) {
|
||||||
$this->getIO()->writeError('Changed CWD to ' . getcwd());
|
$io->writeError('Changed CWD to ' . getcwd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class Application extends BaseApplication
|
||||||
foreach ($composer['scripts'] as $script => $dummy) {
|
foreach ($composer['scripts'] as $script => $dummy) {
|
||||||
if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
|
if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
|
||||||
if ($this->has($script)) {
|
if ($this->has($script)) {
|
||||||
$this->getIO()->writeError('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
|
$io->writeError('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
|
||||||
} else {
|
} else {
|
||||||
$this->add(new Command\ScriptAliasCommand($script));
|
$this->add(new Command\ScriptAliasCommand($script));
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ class Application extends BaseApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($startTime)) {
|
if (isset($startTime)) {
|
||||||
$this->getIO()->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
|
$io->writeError('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -159,14 +159,14 @@ class Application extends BaseApplication
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InputInterface $input
|
* @param InputInterface $input
|
||||||
* @return string
|
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getNewWorkingDir(InputInterface $input)
|
private function getNewWorkingDir(InputInterface $input)
|
||||||
{
|
{
|
||||||
$workingDir = $input->getParameterOption(array('--working-dir', '-d'));
|
$workingDir = $input->getParameterOption(array('--working-dir', '-d'));
|
||||||
if (false !== $workingDir && !is_dir($workingDir)) {
|
if (false !== $workingDir && !is_dir($workingDir)) {
|
||||||
throw new \RuntimeException('Invalid working directory specified.');
|
throw new \RuntimeException('Invalid working directory specified, '.$workingDir.' does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $workingDir;
|
return $workingDir;
|
||||||
|
@ -177,30 +177,32 @@ class Application extends BaseApplication
|
||||||
*/
|
*/
|
||||||
public function renderException($exception, $output)
|
public function renderException($exception, $output)
|
||||||
{
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$composer = $this->getComposer(false, true);
|
$composer = $this->getComposer(false, true);
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
$config = $composer->getConfig();
|
$config = $composer->getConfig();
|
||||||
|
|
||||||
$minSpaceFree = 1024*1024;
|
$minSpaceFree = 1024 * 1024;
|
||||||
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|
||||||
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|
||||||
|| (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
|
|| (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
|
||||||
) {
|
) {
|
||||||
$this->getIO()->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
|
$io->writeError('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
|
if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
|
||||||
$this->getIO()->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
|
$io->writeError('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
|
||||||
$this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
|
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
|
if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
|
||||||
$this->getIO()->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
|
$io->writeError('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
|
||||||
$this->getIO()->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
|
$io->writeError('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($output instanceof ConsoleOutputInterface) {
|
if ($output instanceof ConsoleOutputInterface) {
|
||||||
|
@ -273,6 +275,7 @@ class Application extends BaseApplication
|
||||||
$commands[] = new Command\SearchCommand();
|
$commands[] = new Command\SearchCommand();
|
||||||
$commands[] = new Command\ValidateCommand();
|
$commands[] = new Command\ValidateCommand();
|
||||||
$commands[] = new Command\ShowCommand();
|
$commands[] = new Command\ShowCommand();
|
||||||
|
$commands[] = new Command\SuggestsCommand();
|
||||||
$commands[] = new Command\RequireCommand();
|
$commands[] = new Command\RequireCommand();
|
||||||
$commands[] = new Command\DumpAutoloadCommand();
|
$commands[] = new Command\DumpAutoloadCommand();
|
||||||
$commands[] = new Command\StatusCommand();
|
$commands[] = new Command\StatusCommand();
|
||||||
|
@ -321,15 +324,4 @@ class Application extends BaseApplication
|
||||||
|
|
||||||
return $definition;
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
protected function getDefaultHelperSet()
|
|
||||||
{
|
|
||||||
$helperSet = parent::getDefaultHelperSet();
|
|
||||||
$helperSet->set(new DialogHelper());
|
|
||||||
|
|
||||||
return $helperSet;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class HtmlOutputFormatter extends OutputFormatter
|
||||||
34 => 'blue',
|
34 => 'blue',
|
||||||
35 => 'magenta',
|
35 => 'magenta',
|
||||||
36 => 'cyan',
|
36 => 'cyan',
|
||||||
37 => 'white'
|
37 => 'white',
|
||||||
);
|
);
|
||||||
private static $availableBackgroundColors = array(
|
private static $availableBackgroundColors = array(
|
||||||
40 => 'black',
|
40 => 'black',
|
||||||
|
@ -37,7 +37,7 @@ class HtmlOutputFormatter extends OutputFormatter
|
||||||
44 => 'blue',
|
44 => 'blue',
|
||||||
45 => 'magenta',
|
45 => 'magenta',
|
||||||
46 => 'cyan',
|
46 => 'cyan',
|
||||||
47 => 'white'
|
47 => 'white',
|
||||||
);
|
);
|
||||||
private static $availableOptions = array(
|
private static $availableOptions = array(
|
||||||
1 => 'bold',
|
1 => 'bold',
|
||||||
|
@ -59,7 +59,9 @@ class HtmlOutputFormatter extends OutputFormatter
|
||||||
{
|
{
|
||||||
$formatted = parent::format($message);
|
$formatted = parent::format($message);
|
||||||
|
|
||||||
return preg_replace_callback("{\033\[([0-9;]+)m(.*?)\033\[0m}s", array($this, 'formatHtml'), $formatted);
|
$clearEscapeCodes = '(?:39|49|0|22|24|25|27|28)';
|
||||||
|
|
||||||
|
return preg_replace_callback("{\033\[([0-9;]+)m(.*?)\033\[(?:".$clearEscapeCodes.";)*?".$clearEscapeCodes."m}s", array($this, 'formatHtml'), $formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatHtml($matches)
|
private function formatHtml($matches)
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer\DependencyResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
|
||||||
*/
|
|
||||||
class DebugSolver extends Solver
|
|
||||||
{
|
|
||||||
protected function printDecisionMap()
|
|
||||||
{
|
|
||||||
echo "\nDecisionMap: \n";
|
|
||||||
foreach ($this->decisionMap as $packageId => $level) {
|
|
||||||
if ($packageId === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($level > 0) {
|
|
||||||
echo ' +' . $this->pool->packageById($packageId)."\n";
|
|
||||||
} elseif ($level < 0) {
|
|
||||||
echo ' -' . $this->pool->packageById($packageId)."\n";
|
|
||||||
} else {
|
|
||||||
echo ' ?' . $this->pool->packageById($packageId)."\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function printDecisionQueue()
|
|
||||||
{
|
|
||||||
echo "DecisionQueue: \n";
|
|
||||||
foreach ($this->decisionQueue as $i => $literal) {
|
|
||||||
echo ' ' . $this->pool->literalToString($literal) . ' ' . $this->decisionQueueWhy[$i]." level ".$this->decisionMap[abs($literal)]."\n";
|
|
||||||
}
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function printWatches()
|
|
||||||
{
|
|
||||||
echo "\nWatches:\n";
|
|
||||||
foreach ($this->watches as $literalId => $watch) {
|
|
||||||
echo ' '.$this->literalFromId($literalId)."\n";
|
|
||||||
$queue = array(array(' ', $watch));
|
|
||||||
|
|
||||||
while (!empty($queue)) {
|
|
||||||
list($indent, $watch) = array_pop($queue);
|
|
||||||
|
|
||||||
echo $indent.$watch;
|
|
||||||
|
|
||||||
if ($watch) {
|
|
||||||
echo ' [id='.$watch->getId().',watch1='.$this->literalFromId($watch->watch1).',watch2='.$this->literalFromId($watch->watch2)."]";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\n";
|
|
||||||
|
|
||||||
if ($watch && ($watch->next1 == $watch || $watch->next2 == $watch)) {
|
|
||||||
if ($watch->next1 == $watch) {
|
|
||||||
echo $indent." 1 *RECURSION*";
|
|
||||||
}
|
|
||||||
if ($watch->next2 == $watch) {
|
|
||||||
echo $indent." 2 *RECURSION*";
|
|
||||||
}
|
|
||||||
} elseif ($watch && ($watch->next1 || $watch->next2)) {
|
|
||||||
$indent = str_replace(array('1', '2'), ' ', $indent);
|
|
||||||
|
|
||||||
array_push($queue, array($indent.' 2 ', $watch->next2));
|
|
||||||
array_push($queue, array($indent.' 1 ', $watch->next1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ namespace Composer\DependencyResolver;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
use Composer\Semver\Constraint\Constraint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
@ -38,8 +38,8 @@ class DefaultPolicy implements PolicyInterface
|
||||||
return BasePackage::$stabilities[$stabA] < BasePackage::$stabilities[$stabB];
|
return BasePackage::$stabilities[$stabA] < BasePackage::$stabilities[$stabB];
|
||||||
}
|
}
|
||||||
|
|
||||||
$constraint = new VersionConstraint($operator, $b->getVersion());
|
$constraint = new Constraint($operator, $b->getVersion());
|
||||||
$version = new VersionConstraint('==', $a->getVersion());
|
$version = new Constraint('==', $a->getVersion());
|
||||||
|
|
||||||
return $constraint->matchSpecific($version, true);
|
return $constraint->matchSpecific($version, true);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,17 @@ class DefaultPolicy implements PolicyInterface
|
||||||
return $pool->getPriority($package->getRepository());
|
return $pool->getPriority($package->getRepository());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Method has been renamed to selectPreferredPackages, you should update usages
|
||||||
|
*/
|
||||||
public function selectPreferedPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
|
public function selectPreferedPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
|
||||||
|
{
|
||||||
|
trigger_error('Method selectPreferedPackages is deprecated and replaced by selectPreferredPackages, please update your usage', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
return $this->selectPreferredPackages($pool, $installedMap, $literals, $requiredPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
|
||||||
{
|
{
|
||||||
$packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals);
|
$packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals);
|
||||||
|
|
||||||
|
@ -74,10 +84,10 @@ class DefaultPolicy implements PolicyInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($packages as &$literals) {
|
foreach ($packages as &$literals) {
|
||||||
$literals = $this->pruneToBestVersion($pool, $literals);
|
|
||||||
|
|
||||||
$literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals);
|
$literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals);
|
||||||
|
|
||||||
|
$literals = $this->pruneToBestVersion($pool, $literals);
|
||||||
|
|
||||||
$literals = $this->pruneRemoteAliases($pool, $literals);
|
$literals = $this->pruneRemoteAliases($pool, $literals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +196,7 @@ class DefaultPolicy implements PolicyInterface
|
||||||
foreach ($source->getReplaces() as $link) {
|
foreach ($source->getReplaces() as $link) {
|
||||||
if ($link->getTarget() === $target->getName()
|
if ($link->getTarget() === $target->getName()
|
||||||
// && (null === $link->getConstraint() ||
|
// && (null === $link->getConstraint() ||
|
||||||
// $link->getConstraint()->matches(new VersionConstraint('==', $target->getVersion())))) {
|
// $link->getConstraint()->matches(new Constraint('==', $target->getVersion())))) {
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\DependencyResolver\Operation;
|
namespace Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Solver install operation.
|
* Solver install operation.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\DependencyResolver\Operation;
|
namespace Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Solver install operation.
|
* Solver install operation.
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
namespace Composer\DependencyResolver\Operation;
|
namespace Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
use Composer\Package\Version\VersionParser;
|
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +45,6 @@ abstract class SolverOperation implements OperationInterface
|
||||||
|
|
||||||
protected function formatVersion(PackageInterface $package)
|
protected function formatVersion(PackageInterface $package)
|
||||||
{
|
{
|
||||||
return VersionParser::formatVersion($package);
|
return $package->getFullPrettyVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ use Composer\Package\PackageInterface;
|
||||||
interface PolicyInterface
|
interface PolicyInterface
|
||||||
{
|
{
|
||||||
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
|
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
|
||||||
|
|
||||||
public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package);
|
public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package);
|
||||||
public function selectPreferedPackages(Pool $pool, array $installedMap, array $literals);
|
|
||||||
|
public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Semver\VersionParser;
|
||||||
use Composer\Package\LinkConstraint\LinkConstraintInterface;
|
use Composer\Semver\Constraint\ConstraintInterface;
|
||||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
use Composer\Semver\Constraint\Constraint;
|
||||||
use Composer\Package\LinkConstraint\EmptyConstraint;
|
use Composer\Semver\Constraint\EmptyConstraint;
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Repository\ComposerRepository;
|
use Composer\Repository\ComposerRepository;
|
||||||
|
@ -31,7 +31,7 @@ use Composer\Package\PackageInterface;
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
*/
|
*/
|
||||||
class Pool
|
class Pool implements \Countable
|
||||||
{
|
{
|
||||||
const MATCH_NAME = -1;
|
const MATCH_NAME = -1;
|
||||||
const MATCH_NONE = 0;
|
const MATCH_NONE = 0;
|
||||||
|
@ -55,7 +55,6 @@ class Pool
|
||||||
|
|
||||||
public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
|
public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
|
||||||
{
|
{
|
||||||
$stabilities = BasePackage::$stabilities;
|
|
||||||
$this->versionParser = new VersionParser;
|
$this->versionParser = new VersionParser;
|
||||||
$this->acceptableStabilities = array();
|
$this->acceptableStabilities = array();
|
||||||
foreach (BasePackage::$stabilities as $stability => $value) {
|
foreach (BasePackage::$stabilities as $stability => $value) {
|
||||||
|
@ -151,27 +150,35 @@ class Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the package object for a given package id.
|
* Retrieves the package object for a given package id.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return PackageInterface
|
* @return PackageInterface
|
||||||
*/
|
*/
|
||||||
public function packageById($id)
|
public function packageById($id)
|
||||||
{
|
{
|
||||||
return $this->packages[$id - 1];
|
return $this->packages[$id - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns how many packages have been loaded into the pool
|
||||||
|
*/
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return count($this->packages);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches all packages providing the given package name and match the constraint
|
* Searches all packages providing the given package name and match the constraint
|
||||||
*
|
*
|
||||||
* @param string $name The package name to be searched for
|
* @param string $name The package name to be searched for
|
||||||
* @param LinkConstraintInterface $constraint A constraint that all returned
|
* @param ConstraintInterface $constraint A constraint that all returned
|
||||||
* packages must match or null to return all
|
* packages must match or null to return all
|
||||||
* @param bool $mustMatchName Whether the name of returned packages
|
* @param bool $mustMatchName Whether the name of returned packages
|
||||||
* must match the given name
|
* must match the given name
|
||||||
* @return PackageInterface[] A set of packages
|
* @return PackageInterface[] A set of packages
|
||||||
*/
|
*/
|
||||||
public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false)
|
public function whatProvides($name, ConstraintInterface $constraint = null, $mustMatchName = false)
|
||||||
{
|
{
|
||||||
$key = ((int) $mustMatchName).$constraint;
|
$key = ((int) $mustMatchName).$constraint;
|
||||||
if (isset($this->providerCache[$name][$key])) {
|
if (isset($this->providerCache[$name][$key])) {
|
||||||
|
@ -271,11 +278,6 @@ class Pool
|
||||||
return $this->packageById($packageId);
|
return $this->packageById($packageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function literalToString($literal)
|
|
||||||
{
|
|
||||||
return ($literal > 0 ? '+' : '-') . $this->literalToPackage($literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function literalToPrettyString($literal, $installedMap)
|
public function literalToPrettyString($literal, $installedMap)
|
||||||
{
|
{
|
||||||
$package = $this->literalToPackage($literal);
|
$package = $this->literalToPackage($literal);
|
||||||
|
@ -310,12 +312,12 @@ class Pool
|
||||||
* Checks if the package matches the given constraint directly or through
|
* Checks if the package matches the given constraint directly or through
|
||||||
* provided or replaced packages
|
* provided or replaced packages
|
||||||
*
|
*
|
||||||
* @param array|PackageInterface $candidate
|
* @param array|PackageInterface $candidate
|
||||||
* @param string $name Name of the package to be matched
|
* @param string $name Name of the package to be matched
|
||||||
* @param LinkConstraintInterface $constraint The constraint to verify
|
* @param ConstraintInterface $constraint The constraint to verify
|
||||||
* @return int One of the MATCH* constants of this class or 0 if there is no match
|
* @return int One of the MATCH* constants of this class or 0 if there is no match
|
||||||
*/
|
*/
|
||||||
private function match($candidate, $name, LinkConstraintInterface $constraint = null)
|
private function match($candidate, $name, ConstraintInterface $constraint = null)
|
||||||
{
|
{
|
||||||
$candidateName = $candidate->getName();
|
$candidateName = $candidate->getName();
|
||||||
$candidateVersion = $candidate->getVersion();
|
$candidateVersion = $candidate->getVersion();
|
||||||
|
@ -329,7 +331,7 @@ class Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($candidateName === $name) {
|
if ($candidateName === $name) {
|
||||||
$pkgConstraint = new VersionConstraint('==', $candidateVersion);
|
$pkgConstraint = new Constraint('==', $candidateVersion);
|
||||||
|
|
||||||
if ($constraint === null || $constraint->matches($pkgConstraint)) {
|
if ($constraint === null || $constraint->matches($pkgConstraint)) {
|
||||||
return $requireFilter->matches($pkgConstraint) ? self::MATCH : self::MATCH_FILTERED;
|
return $requireFilter->matches($pkgConstraint) ? self::MATCH : self::MATCH_FILTERED;
|
||||||
|
|
|
@ -47,7 +47,7 @@ class Problem
|
||||||
*/
|
*/
|
||||||
public function addRule(Rule $rule)
|
public function addRule(Rule $rule)
|
||||||
{
|
{
|
||||||
$this->addReason($rule->getId(), array(
|
$this->addReason(spl_object_hash($rule), array(
|
||||||
'rule' => $rule,
|
'rule' => $rule,
|
||||||
'job' => $rule->getJob(),
|
'job' => $rule->getJob(),
|
||||||
));
|
));
|
||||||
|
@ -87,8 +87,12 @@ class Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($job && $job['cmd'] === 'install' && empty($packages)) {
|
if ($job && $job['cmd'] === 'install' && empty($packages)) {
|
||||||
|
|
||||||
// handle php/hhvm
|
// handle php/hhvm
|
||||||
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
|
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
|
||||||
|
$available = $this->pool->whatProvides($job['packageName']);
|
||||||
|
$version = count($available) ? $available[0]->getPrettyVersion() : phpversion();
|
||||||
|
|
||||||
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
|
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
|
||||||
|
|
||||||
if (defined('HHVM_VERSION')) {
|
if (defined('HHVM_VERSION')) {
|
||||||
|
@ -97,7 +101,7 @@ class Problem
|
||||||
return $msg . 'you are running this with PHP and not HHVM.';
|
return $msg . 'you are running this with PHP and not HHVM.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $msg . 'your PHP version does not satisfy that requirement.';
|
return $msg . 'your PHP version ('. $version .') does not satisfy that requirement.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle php extensions
|
// handle php extensions
|
||||||
|
@ -218,7 +222,7 @@ class Problem
|
||||||
/**
|
/**
|
||||||
* Turns a constraint into text usable in a sentence describing a job
|
* Turns a constraint into text usable in a sentence describing a job
|
||||||
*
|
*
|
||||||
* @param \Composer\Package\LinkConstraint\LinkConstraintInterface $constraint
|
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function constraintToText($constraint)
|
protected function constraintToText($constraint)
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
namespace Composer\DependencyResolver;
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\Package\LinkConstraint\LinkConstraintInterface;
|
use Composer\Semver\Constraint\ConstraintInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
@ -20,25 +20,23 @@ use Composer\Package\LinkConstraint\LinkConstraintInterface;
|
||||||
class Request
|
class Request
|
||||||
{
|
{
|
||||||
protected $jobs;
|
protected $jobs;
|
||||||
protected $pool;
|
|
||||||
|
|
||||||
public function __construct(Pool $pool)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->pool = $pool;
|
|
||||||
$this->jobs = array();
|
$this->jobs = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function install($packageName, LinkConstraintInterface $constraint = null)
|
public function install($packageName, ConstraintInterface $constraint = null)
|
||||||
{
|
{
|
||||||
$this->addJob($packageName, 'install', $constraint);
|
$this->addJob($packageName, 'install', $constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update($packageName, LinkConstraintInterface $constraint = null)
|
public function update($packageName, ConstraintInterface $constraint = null)
|
||||||
{
|
{
|
||||||
$this->addJob($packageName, 'update', $constraint);
|
$this->addJob($packageName, 'update', $constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remove($packageName, LinkConstraintInterface $constraint = null)
|
public function remove($packageName, ConstraintInterface $constraint = null)
|
||||||
{
|
{
|
||||||
$this->addJob($packageName, 'remove', $constraint);
|
$this->addJob($packageName, 'remove', $constraint);
|
||||||
}
|
}
|
||||||
|
@ -48,12 +46,12 @@ class Request
|
||||||
*
|
*
|
||||||
* These jobs will not be tempered with by the solver
|
* These jobs will not be tempered with by the solver
|
||||||
*/
|
*/
|
||||||
public function fix($packageName, LinkConstraintInterface $constraint = null)
|
public function fix($packageName, ConstraintInterface $constraint = null)
|
||||||
{
|
{
|
||||||
$this->addJob($packageName, 'install', $constraint, true);
|
$this->addJob($packageName, 'install', $constraint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null, $fixed = false)
|
protected function addJob($packageName, $cmd, ConstraintInterface $constraint = null, $fixed = false)
|
||||||
{
|
{
|
||||||
$packageName = strtolower($packageName);
|
$packageName = strtolower($packageName);
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ class Request
|
||||||
'cmd' => $cmd,
|
'cmd' => $cmd,
|
||||||
'packageName' => $packageName,
|
'packageName' => $packageName,
|
||||||
'constraint' => $constraint,
|
'constraint' => $constraint,
|
||||||
'fixed' => $fixed
|
'fixed' => $fixed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,63 +29,51 @@ class Rule
|
||||||
const RULE_LEARNED = 12;
|
const RULE_LEARNED = 12;
|
||||||
const RULE_PACKAGE_ALIAS = 13;
|
const RULE_PACKAGE_ALIAS = 13;
|
||||||
|
|
||||||
|
const BITFIELD_TYPE = 0;
|
||||||
|
const BITFIELD_REASON = 8;
|
||||||
|
const BITFIELD_DISABLED = 16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* READ-ONLY: The literals this rule consists of.
|
* READ-ONLY: The literals this rule consists of.
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $literals;
|
public $literals;
|
||||||
|
|
||||||
protected $disabled;
|
protected $bitfield;
|
||||||
protected $type;
|
|
||||||
protected $id;
|
|
||||||
protected $reason;
|
|
||||||
protected $reasonData;
|
protected $reasonData;
|
||||||
|
|
||||||
protected $job;
|
|
||||||
|
|
||||||
protected $ruleHash;
|
|
||||||
|
|
||||||
public function __construct(array $literals, $reason, $reasonData, $job = null)
|
public function __construct(array $literals, $reason, $reasonData, $job = null)
|
||||||
{
|
{
|
||||||
// sort all packages ascending by id
|
// sort all packages ascending by id
|
||||||
sort($literals);
|
sort($literals);
|
||||||
|
|
||||||
$this->literals = $literals;
|
$this->literals = $literals;
|
||||||
$this->reason = $reason;
|
|
||||||
$this->reasonData = $reasonData;
|
$this->reasonData = $reasonData;
|
||||||
|
|
||||||
$this->disabled = false;
|
if ($job) {
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
$this->job = $job;
|
$this->bitfield = (0 << self::BITFIELD_DISABLED) |
|
||||||
|
($reason << self::BITFIELD_REASON) |
|
||||||
$this->type = -1;
|
(255 << self::BITFIELD_TYPE);
|
||||||
|
|
||||||
$this->ruleHash = substr(md5(implode(',', $this->literals)), 0, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHash()
|
public function getHash()
|
||||||
{
|
{
|
||||||
return $this->ruleHash;
|
$data = unpack('ihash', md5(implode(',', $this->literals), true));
|
||||||
}
|
|
||||||
|
|
||||||
public function setId($id)
|
return $data['hash'];
|
||||||
{
|
|
||||||
$this->id = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId()
|
|
||||||
{
|
|
||||||
return $this->id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getJob()
|
public function getJob()
|
||||||
{
|
{
|
||||||
return $this->job;
|
return isset($this->job) ? $this->job : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReason()
|
public function getReason()
|
||||||
{
|
{
|
||||||
return $this->reason;
|
return ($this->bitfield & (255 << self::BITFIELD_REASON)) >> self::BITFIELD_REASON;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReasonData()
|
public function getReasonData()
|
||||||
|
@ -95,11 +83,11 @@ class Rule
|
||||||
|
|
||||||
public function getRequiredPackage()
|
public function getRequiredPackage()
|
||||||
{
|
{
|
||||||
if ($this->reason === self::RULE_JOB_INSTALL) {
|
if ($this->getReason() === self::RULE_JOB_INSTALL) {
|
||||||
return $this->reasonData;
|
return $this->reasonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->reason === self::RULE_PACKAGE_REQUIRES) {
|
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
|
||||||
return $this->reasonData->getTarget();
|
return $this->reasonData->getTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,10 +102,6 @@ class Rule
|
||||||
*/
|
*/
|
||||||
public function equals(Rule $rule)
|
public function equals(Rule $rule)
|
||||||
{
|
{
|
||||||
if ($this->ruleHash !== $rule->ruleHash) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($this->literals) != count($rule->literals)) {
|
if (count($this->literals) != count($rule->literals)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -133,32 +117,32 @@ class Rule
|
||||||
|
|
||||||
public function setType($type)
|
public function setType($type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->bitfield = ($this->bitfield & ~(255 << self::BITFIELD_TYPE)) | ((255 & $type) << self::BITFIELD_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getType()
|
public function getType()
|
||||||
{
|
{
|
||||||
return $this->type;
|
return ($this->bitfield & (255 << self::BITFIELD_TYPE)) >> self::BITFIELD_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function disable()
|
public function disable()
|
||||||
{
|
{
|
||||||
$this->disabled = true;
|
$this->bitfield = ($this->bitfield & ~(255 << self::BITFIELD_DISABLED)) | (1 << self::BITFIELD_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enable()
|
public function enable()
|
||||||
{
|
{
|
||||||
$this->disabled = false;
|
$this->bitfield = $this->bitfield & ~(255 << self::BITFIELD_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDisabled()
|
public function isDisabled()
|
||||||
{
|
{
|
||||||
return $this->disabled;
|
return (bool) (($this->bitfield & (255 << self::BITFIELD_DISABLED)) >> self::BITFIELD_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnabled()
|
public function isEnabled()
|
||||||
{
|
{
|
||||||
return !$this->disabled;
|
return !(($this->bitfield & (255 << self::BITFIELD_DISABLED)) >> self::BITFIELD_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,7 +168,7 @@ class Rule
|
||||||
$ruleText .= $pool->literalToPrettyString($literal, $installedMap);
|
$ruleText .= $pool->literalToPrettyString($literal, $installedMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->reason) {
|
switch ($this->getReason()) {
|
||||||
case self::RULE_INTERNAL_ALLOW_UPDATE:
|
case self::RULE_INTERNAL_ALLOW_UPDATE:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
|
|
||||||
|
@ -216,16 +200,17 @@ class Rule
|
||||||
} else {
|
} else {
|
||||||
$targetName = $this->reasonData->getTarget();
|
$targetName = $this->reasonData->getTarget();
|
||||||
|
|
||||||
// handle php extensions
|
|
||||||
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
|
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
|
||||||
|
// handle php/hhvm
|
||||||
if (defined('HHVM_VERSION')) {
|
if (defined('HHVM_VERSION')) {
|
||||||
$text .= ' -> your HHVM version does not satisfy that requirement.';
|
$text .= ' -> your HHVM version does not satisfy that requirement.';
|
||||||
} elseif ($targetName === 'hhvm') {
|
} elseif ($targetName === 'hhvm') {
|
||||||
$text .= ' -> you are running this with PHP and not HHVM.';
|
$text .= ' -> you are running this with PHP and not HHVM.';
|
||||||
} else {
|
} else {
|
||||||
$text .= ' -> your PHP version does not satisfy that requirement.';
|
$text .= ' -> your PHP version ('. phpversion() .') or "config.platform.php" value does not satisfy that requirement.';
|
||||||
}
|
}
|
||||||
} elseif (0 === strpos($targetName, 'ext-')) {
|
} elseif (0 === strpos($targetName, 'ext-')) {
|
||||||
|
// handle php extensions
|
||||||
$ext = substr($targetName, 4);
|
$ext = substr($targetName, 4);
|
||||||
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
|
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
public $ruleById;
|
public $ruleById;
|
||||||
|
|
||||||
protected static $types = array(
|
protected static $types = array(
|
||||||
-1 => 'UNKNOWN',
|
255 => 'UNKNOWN',
|
||||||
self::TYPE_PACKAGE => 'PACKAGE',
|
self::TYPE_PACKAGE => 'PACKAGE',
|
||||||
self::TYPE_JOB => 'JOB',
|
self::TYPE_JOB => 'JOB',
|
||||||
self::TYPE_LEARNED => 'LEARNED',
|
self::TYPE_LEARNED => 'LEARNED',
|
||||||
|
@ -66,7 +66,6 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
$this->ruleById[$this->nextRuleId] = $rule;
|
$this->ruleById[$this->nextRuleId] = $rule;
|
||||||
$rule->setType($type);
|
$rule->setType($type);
|
||||||
|
|
||||||
$rule->setId($this->nextRuleId);
|
|
||||||
$this->nextRuleId++;
|
$this->nextRuleId++;
|
||||||
|
|
||||||
$hash = $rule->getHash();
|
$hash = $rule->getHash();
|
||||||
|
@ -131,7 +130,7 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
public function getTypes()
|
public function getTypes()
|
||||||
{
|
{
|
||||||
$types = self::$types;
|
$types = self::$types;
|
||||||
unset($types[-1]);
|
unset($types[255]);
|
||||||
|
|
||||||
return array_keys($types);
|
return array_keys($types);
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ class RuleWatchNode
|
||||||
/**
|
/**
|
||||||
* Given one watched literal, this method returns the other watched literal
|
* Given one watched literal, this method returns the other watched literal
|
||||||
*
|
*
|
||||||
* @param int The watched literal that should not be returned
|
* @param int $literal The watched literal that should not be returned
|
||||||
* @return int A literal
|
* @return int A literal
|
||||||
*/
|
*/
|
||||||
public function getOtherWatch($literal)
|
public function getOtherWatch($literal)
|
||||||
|
|
|
@ -23,25 +23,44 @@ class Solver
|
||||||
const BRANCH_LITERALS = 0;
|
const BRANCH_LITERALS = 0;
|
||||||
const BRANCH_LEVEL = 1;
|
const BRANCH_LEVEL = 1;
|
||||||
|
|
||||||
|
/** @var PolicyInterface */
|
||||||
protected $policy;
|
protected $policy;
|
||||||
|
/** @var Pool */
|
||||||
protected $pool;
|
protected $pool;
|
||||||
|
/** @var RepositoryInterface */
|
||||||
protected $installed;
|
protected $installed;
|
||||||
|
/** @var Ruleset */
|
||||||
protected $rules;
|
protected $rules;
|
||||||
|
/** @var RuleSetGenerator */
|
||||||
protected $ruleSetGenerator;
|
protected $ruleSetGenerator;
|
||||||
protected $updateAll;
|
/** @var array */
|
||||||
|
protected $jobs;
|
||||||
|
|
||||||
protected $addedMap = array();
|
/** @var int[] */
|
||||||
protected $updateMap = array();
|
protected $updateMap = array();
|
||||||
|
/** @var RuleWatchGraph */
|
||||||
protected $watchGraph;
|
protected $watchGraph;
|
||||||
|
/** @var Decisions */
|
||||||
protected $decisions;
|
protected $decisions;
|
||||||
|
/** @var int[] */
|
||||||
protected $installedMap;
|
protected $installedMap;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
protected $propagateIndex;
|
protected $propagateIndex;
|
||||||
|
/** @var array[] */
|
||||||
protected $branches = array();
|
protected $branches = array();
|
||||||
|
/** @var Problem[] */
|
||||||
protected $problems = array();
|
protected $problems = array();
|
||||||
|
/** @var array */
|
||||||
protected $learnedPool = array();
|
protected $learnedPool = array();
|
||||||
|
/** @var array */
|
||||||
protected $learnedWhy = array();
|
protected $learnedWhy = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PolicyInterface $policy
|
||||||
|
* @param Pool $pool
|
||||||
|
* @param RepositoryInterface $installed
|
||||||
|
*/
|
||||||
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
|
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
|
||||||
{
|
{
|
||||||
$this->policy = $policy;
|
$this->policy = $policy;
|
||||||
|
@ -50,7 +69,16 @@ class Solver
|
||||||
$this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
|
$this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getRuleSetSize()
|
||||||
|
{
|
||||||
|
return count($this->rules);
|
||||||
|
}
|
||||||
|
|
||||||
// aka solver_makeruledecisions
|
// aka solver_makeruledecisions
|
||||||
|
|
||||||
private function makeAssertionRuleDecisions()
|
private function makeAssertionRuleDecisions()
|
||||||
{
|
{
|
||||||
$decisionStart = count($this->decisions) - 1;
|
$decisionStart = count($this->decisions) - 1;
|
||||||
|
@ -130,6 +158,9 @@ class Solver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $ignorePlatformReqs
|
||||||
|
*/
|
||||||
protected function checkForRootRequireProblems($ignorePlatformReqs)
|
protected function checkForRootRequireProblems($ignorePlatformReqs)
|
||||||
{
|
{
|
||||||
foreach ($this->jobs as $job) {
|
foreach ($this->jobs as $job) {
|
||||||
|
@ -164,6 +195,11 @@ class Solver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param bool $ignorePlatformReqs
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function solve(Request $request, $ignorePlatformReqs = false)
|
public function solve(Request $request, $ignorePlatformReqs = false)
|
||||||
{
|
{
|
||||||
$this->jobs = $request->getJobs();
|
$this->jobs = $request->getJobs();
|
||||||
|
@ -199,20 +235,13 @@ class Solver
|
||||||
return $transaction->getOperations();
|
return $transaction->getOperations();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function literalFromId($id)
|
|
||||||
{
|
|
||||||
$package = $this->pool->packageById(abs($id));
|
|
||||||
|
|
||||||
return new Literal($package, $id > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a decision and propagates it to all rules.
|
* Makes a decision and propagates it to all rules.
|
||||||
*
|
*
|
||||||
* Evaluates each term affected by the decision (linked through watches)
|
* Evaluates each term affected by the decision (linked through watches)
|
||||||
* If we find unit rules we make new decisions based on them
|
* If we find unit rules we make new decisions based on them
|
||||||
*
|
*
|
||||||
* @param integer $level
|
* @param int $level
|
||||||
* @return Rule|null A rule on conflict, otherwise null.
|
* @return Rule|null A rule on conflict, otherwise null.
|
||||||
*/
|
*/
|
||||||
protected function propagate($level)
|
protected function propagate($level)
|
||||||
|
@ -238,6 +267,8 @@ class Solver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts a decision at the given level.
|
* Reverts a decision at the given level.
|
||||||
|
*
|
||||||
|
* @param int $level
|
||||||
*/
|
*/
|
||||||
private function revert($level)
|
private function revert($level)
|
||||||
{
|
{
|
||||||
|
@ -263,8 +294,7 @@ class Solver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**-------------------------------------------------------------------
|
/**
|
||||||
*
|
|
||||||
* setpropagatelearn
|
* setpropagatelearn
|
||||||
*
|
*
|
||||||
* add free decision (a positive literal) to decision queue
|
* add free decision (a positive literal) to decision queue
|
||||||
|
@ -277,6 +307,11 @@ class Solver
|
||||||
*
|
*
|
||||||
* returns the new solver level or 0 if unsolvable
|
* returns the new solver level or 0 if unsolvable
|
||||||
*
|
*
|
||||||
|
* @param int $level
|
||||||
|
* @param string|int $literal
|
||||||
|
* @param bool $disableRules
|
||||||
|
* @param Rule $rule
|
||||||
|
* @return int
|
||||||
*/
|
*/
|
||||||
private function setPropagateLearn($level, $literal, $disableRules, Rule $rule)
|
private function setPropagateLearn($level, $literal, $disableRules, Rule $rule)
|
||||||
{
|
{
|
||||||
|
@ -314,7 +349,7 @@ class Solver
|
||||||
|
|
||||||
$this->rules->add($newRule, RuleSet::TYPE_LEARNED);
|
$this->rules->add($newRule, RuleSet::TYPE_LEARNED);
|
||||||
|
|
||||||
$this->learnedWhy[$newRule->getId()] = $why;
|
$this->learnedWhy[spl_object_hash($newRule)] = $why;
|
||||||
|
|
||||||
$ruleNode = new RuleWatchNode($newRule);
|
$ruleNode = new RuleWatchNode($newRule);
|
||||||
$ruleNode->watch2OnHighest($this->decisions);
|
$ruleNode->watch2OnHighest($this->decisions);
|
||||||
|
@ -326,10 +361,17 @@ class Solver
|
||||||
return $level;
|
return $level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $level
|
||||||
|
* @param array $decisionQueue
|
||||||
|
* @param bool $disableRules
|
||||||
|
* @param Rule $rule
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
|
private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
|
||||||
{
|
{
|
||||||
// choose best package to install from decisionQueue
|
// choose best package to install from decisionQueue
|
||||||
$literals = $this->policy->selectPreferedPackages($this->pool, $this->installedMap, $decisionQueue, $rule->getRequiredPackage());
|
$literals = $this->policy->selectPreferredPackages($this->pool, $this->installedMap, $decisionQueue, $rule->getRequiredPackage());
|
||||||
|
|
||||||
$selectedLiteral = array_shift($literals);
|
$selectedLiteral = array_shift($literals);
|
||||||
|
|
||||||
|
@ -341,7 +383,12 @@ class Solver
|
||||||
return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule);
|
return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function analyze($level, $rule)
|
/**
|
||||||
|
* @param int $level
|
||||||
|
* @param Rule $rule
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function analyze($level, Rule $rule)
|
||||||
{
|
{
|
||||||
$analyzedRule = $rule;
|
$analyzedRule = $rule;
|
||||||
$ruleLevel = 1;
|
$ruleLevel = 1;
|
||||||
|
@ -447,9 +494,13 @@ class Solver
|
||||||
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
|
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function analyzeUnsolvableRule($problem, $conflictRule)
|
/**
|
||||||
|
* @param Problem $problem
|
||||||
|
* @param Rule $conflictRule
|
||||||
|
*/
|
||||||
|
private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule)
|
||||||
{
|
{
|
||||||
$why = $conflictRule->getId();
|
$why = spl_object_hash($conflictRule);
|
||||||
|
|
||||||
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
|
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
|
||||||
$learnedWhy = $this->learnedWhy[$why];
|
$learnedWhy = $this->learnedWhy[$why];
|
||||||
|
@ -471,7 +522,12 @@ class Solver
|
||||||
$problem->addRule($conflictRule);
|
$problem->addRule($conflictRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function analyzeUnsolvable($conflictRule, $disableRules)
|
/**
|
||||||
|
* @param Rule $conflictRule
|
||||||
|
* @param bool $disableRules
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function analyzeUnsolvable(Rule $conflictRule, $disableRules)
|
||||||
{
|
{
|
||||||
$problem = new Problem($this->pool);
|
$problem = new Problem($this->pool);
|
||||||
$problem->addRule($conflictRule);
|
$problem->addRule($conflictRule);
|
||||||
|
@ -528,7 +584,10 @@ class Solver
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function disableProblem($why)
|
/**
|
||||||
|
* @param Rule $why
|
||||||
|
*/
|
||||||
|
private function disableProblem(Rule $why)
|
||||||
{
|
{
|
||||||
$job = $why->getJob();
|
$job = $why->getJob();
|
||||||
|
|
||||||
|
@ -540,6 +599,7 @@ class Solver
|
||||||
|
|
||||||
// disable all rules of this job
|
// disable all rules of this job
|
||||||
foreach ($this->rules as $rule) {
|
foreach ($this->rules as $rule) {
|
||||||
|
/** @var Rule $rule */
|
||||||
if ($job === $rule->getJob()) {
|
if ($job === $rule->getJob()) {
|
||||||
$rule->disable();
|
$rule->disable();
|
||||||
}
|
}
|
||||||
|
@ -557,17 +617,17 @@ class Solver
|
||||||
$this->makeAssertionRuleDecisions();
|
$this->makeAssertionRuleDecisions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/**
|
||||||
* enable/disable learnt rules
|
* enable/disable learnt rules
|
||||||
*
|
*
|
||||||
* we have enabled or disabled some of our rules. We now re-enable all
|
* we have enabled or disabled some of our rules. We now re-enable all
|
||||||
* of our learnt rules except the ones that were learnt from rules that
|
* of our learnt rules except the ones that were learnt from rules that
|
||||||
* are now disabled.
|
* are now disabled.
|
||||||
*/
|
*/
|
||||||
private function enableDisableLearnedRules()
|
private function enableDisableLearnedRules()
|
||||||
{
|
{
|
||||||
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
|
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
|
||||||
$why = $this->learnedWhy[$rule->getId()];
|
$why = $this->learnedWhy[spl_object_hash($rule)];
|
||||||
$problemRules = $this->learnedPool[$why];
|
$problemRules = $this->learnedPool[$why];
|
||||||
|
|
||||||
$foundDisabled = false;
|
$foundDisabled = false;
|
||||||
|
@ -586,22 +646,28 @@ class Solver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $disableRules
|
||||||
|
*/
|
||||||
private function runSat($disableRules = true)
|
private function runSat($disableRules = true)
|
||||||
{
|
{
|
||||||
$this->propagateIndex = 0;
|
$this->propagateIndex = 0;
|
||||||
|
|
||||||
// /*
|
/*
|
||||||
// * here's the main loop:
|
* here's the main loop:
|
||||||
// * 1) propagate new decisions (only needed once)
|
* 1) propagate new decisions (only needed once)
|
||||||
// * 2) fulfill jobs
|
* 2) fulfill jobs
|
||||||
// * 3) fulfill all unresolved rules
|
* 3) fulfill all unresolved rules
|
||||||
// * 4) minimalize solution if we had choices
|
* 4) minimalize solution if we had choices
|
||||||
// * if we encounter a problem, we rewind to a safe level and restart
|
* if we encounter a problem, we rewind to a safe level and restart
|
||||||
// * with step 1
|
* with step 1
|
||||||
// */
|
*/
|
||||||
|
|
||||||
$decisionQueue = array();
|
$decisionQueue = array();
|
||||||
$decisionSupplementQueue = array();
|
$decisionSupplementQueue = array();
|
||||||
|
/**
|
||||||
|
* @todo this makes $disableRules always false; determine the rationale and possibly remove dead code?
|
||||||
|
*/
|
||||||
$disableRules = array();
|
$disableRules = array();
|
||||||
|
|
||||||
$level = 1;
|
$level = 1;
|
||||||
|
|
|
@ -32,11 +32,11 @@ class SolverProblemsException extends \RuntimeException
|
||||||
{
|
{
|
||||||
$text = "\n";
|
$text = "\n";
|
||||||
foreach ($this->problems as $i => $problem) {
|
foreach ($this->problems as $i => $problem) {
|
||||||
$text .= " Problem ".($i+1).$problem->getPrettyString($this->installedMap)."\n";
|
$text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) {
|
if (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://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.\n\nRead <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
|
$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://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.\n\nRead <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $text;
|
return $text;
|
||||||
|
|
|
@ -77,7 +77,11 @@ abstract class ArchiveDownloader extends FileDownloader
|
||||||
|
|
||||||
// retry downloading if we have an invalid zip file
|
// retry downloading if we have an invalid zip file
|
||||||
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
|
if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
|
||||||
$this->io->writeError(' Invalid zip file, retrying...');
|
if ($this->io->isDebug()) {
|
||||||
|
$this->io->writeError(' Invalid zip file ('.$e->getMessage().'), retrying...');
|
||||||
|
} else {
|
||||||
|
$this->io->writeError(' Invalid zip file, retrying...');
|
||||||
|
}
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -115,10 +119,11 @@ abstract class ArchiveDownloader extends FileDownloader
|
||||||
// update api archives to the proper reference
|
// update api archives to the proper reference
|
||||||
$url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
|
$url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
|
||||||
}
|
}
|
||||||
}
|
} elseif ($package->getDistReference() && strpos($url, 'bitbucket.org')) {
|
||||||
|
if (preg_match('{^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$}i', $url, $match)) {
|
||||||
if (!extension_loaded('openssl') && (0 === strpos($url, 'https:') || 0 === strpos($url, 'http://github.com'))) {
|
// update Bitbucket archives to the proper reference
|
||||||
throw new \RuntimeException('You must enable the openssl extension to download files via https');
|
$url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $package->getDistReference() . '.' . $match[4];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::processUrl($package, $url);
|
return parent::processUrl($package, $url);
|
||||||
|
|
|
@ -103,10 +103,9 @@ class DownloadManager
|
||||||
/**
|
/**
|
||||||
* Returns downloader for a specific installation type.
|
* Returns downloader for a specific installation type.
|
||||||
*
|
*
|
||||||
* @param string $type installation type
|
* @param string $type installation type
|
||||||
* @return DownloaderInterface
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException if downloader for provided type is not registered
|
* @throws \InvalidArgumentException if downloader for provided type is not registered
|
||||||
|
* @return DownloaderInterface
|
||||||
*/
|
*/
|
||||||
public function getDownloader($type)
|
public function getDownloader($type)
|
||||||
{
|
{
|
||||||
|
@ -121,12 +120,11 @@ class DownloadManager
|
||||||
/**
|
/**
|
||||||
* Returns downloader for already installed package.
|
* Returns downloader for already installed package.
|
||||||
*
|
*
|
||||||
* @param PackageInterface $package package instance
|
* @param PackageInterface $package package instance
|
||||||
* @return DownloaderInterface|null
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException if package has no installation source specified
|
* @throws \InvalidArgumentException if package has no installation source specified
|
||||||
* @throws \LogicException if specific downloader used to load package with
|
* @throws \LogicException if specific downloader used to load package with
|
||||||
* wrong type
|
* wrong type
|
||||||
|
* @return DownloaderInterface|null
|
||||||
*/
|
*/
|
||||||
public function getDownloaderForInstalledPackage(PackageInterface $package)
|
public function getDownloaderForInstalledPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,6 @@ use Composer\Config;
|
||||||
use Composer\Cache;
|
use Composer\Cache;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\Version\VersionParser;
|
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Composer\Plugin\PreFileDownloadEvent;
|
use Composer\Plugin\PreFileDownloadEvent;
|
||||||
use Composer\EventDispatcher\EventDispatcher;
|
use Composer\EventDispatcher\EventDispatcher;
|
||||||
|
@ -81,7 +80,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
throw new \InvalidArgumentException('The given package is missing url information');
|
throw new \InvalidArgumentException('The given package is missing url information');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
|
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||||
|
|
||||||
$urls = $package->getDistUrls();
|
$urls = $package->getDistUrls();
|
||||||
while ($url = array_shift($urls)) {
|
while ($url = array_shift($urls)) {
|
||||||
|
@ -138,7 +137,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
break;
|
break;
|
||||||
} catch (TransportException $e) {
|
} catch (TransportException $e) {
|
||||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||||
if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
|
if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
if ($this->io->isVerbose()) {
|
if ($this->io->isVerbose()) {
|
||||||
|
@ -205,7 +204,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
*/
|
*/
|
||||||
public function remove(PackageInterface $package, $path)
|
public function remove(PackageInterface $package, $path)
|
||||||
{
|
{
|
||||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
|
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||||
if (!$this->filesystem->removeDirectory($path)) {
|
if (!$this->filesystem->removeDirectory($path)) {
|
||||||
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
|
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
|
||||||
}
|
}
|
||||||
|
@ -226,11 +225,10 @@ class FileDownloader implements DownloaderInterface
|
||||||
/**
|
/**
|
||||||
* Process the download url
|
* Process the download url
|
||||||
*
|
*
|
||||||
* @param PackageInterface $package package the url is coming from
|
* @param PackageInterface $package package the url is coming from
|
||||||
* @param string $url download url
|
* @param string $url download url
|
||||||
* @return string url
|
|
||||||
*
|
|
||||||
* @throws \RuntimeException If any problem with the url
|
* @throws \RuntimeException If any problem with the url
|
||||||
|
* @return string url
|
||||||
*/
|
*/
|
||||||
protected function processUrl(PackageInterface $package, $url)
|
protected function processUrl(PackageInterface $package, $url)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
namespace Composer\Downloader;
|
namespace Composer\Downloader;
|
||||||
|
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Util\GitHub;
|
|
||||||
use Composer\Util\Git as GitUtil;
|
use Composer\Util\Git as GitUtil;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
@ -74,7 +73,7 @@ class GitDownloader extends VcsDownloader
|
||||||
GitUtil::cleanEnv();
|
GitUtil::cleanEnv();
|
||||||
$path = $this->normalizePath($path);
|
$path = $this->normalizePath($path);
|
||||||
if (!is_dir($path.'/.git')) {
|
if (!is_dir($path.'/.git')) {
|
||||||
throw new \RuntimeException('The .git directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
|
throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
|
||||||
}
|
}
|
||||||
|
|
||||||
$ref = $target->getSourceReference();
|
$ref = $target->getSourceReference();
|
||||||
|
@ -82,7 +81,7 @@ class GitDownloader extends VcsDownloader
|
||||||
$command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
|
$command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
|
||||||
|
|
||||||
$commandCallable = function ($url) use ($command) {
|
$commandCallable = function ($url) use ($command) {
|
||||||
return sprintf($command, ProcessExecutor::escape ($url));
|
return sprintf($command, ProcessExecutor::escape($url));
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->gitUtil->runCommand($commandCallable, $url, $path);
|
$this->gitUtil->runCommand($commandCallable, $url, $path);
|
||||||
|
@ -150,7 +149,7 @@ class GitDownloader extends VcsDownloader
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
switch ($this->io->ask(' <info>Discard changes [y,n,v,'.($update ? 's,' : '').'?]?</info> ', '?')) {
|
switch ($this->io->ask(' <info>Discard changes [y,n,v,d,'.($update ? 's,' : '').'?]?</info> ', '?')) {
|
||||||
case 'y':
|
case 'y':
|
||||||
$this->discardChanges($path);
|
$this->discardChanges($path);
|
||||||
break 2;
|
break 2;
|
||||||
|
@ -170,6 +169,10 @@ class GitDownloader extends VcsDownloader
|
||||||
$this->io->writeError($changes);
|
$this->io->writeError($changes);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
$this->viewDiff($path);
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
help:
|
help:
|
||||||
|
@ -177,6 +180,7 @@ class GitDownloader extends VcsDownloader
|
||||||
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
|
' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
|
||||||
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
|
' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
|
||||||
' v - view modified files',
|
' v - view modified files',
|
||||||
|
' d - view local modifications (diff)',
|
||||||
));
|
));
|
||||||
if ($update) {
|
if ($update) {
|
||||||
$this->io->writeError(' s - stash changes and try to reapply them after the update');
|
$this->io->writeError(' s - stash changes and try to reapply them after the update');
|
||||||
|
@ -205,17 +209,21 @@ class GitDownloader extends VcsDownloader
|
||||||
/**
|
/**
|
||||||
* Updates the given path to the given commit ref
|
* Updates the given path to the given commit ref
|
||||||
*
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @param string $reference
|
* @param string $reference
|
||||||
* @param string $branch
|
* @param string $branch
|
||||||
* @param \DateTime $date
|
* @param \DateTime $date
|
||||||
* @return null|string if a string is returned, it is the commit reference that was checked out if the original could not be found
|
|
||||||
*
|
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
* @return null|string if a string is returned, it is the commit reference that was checked out if the original could not be found
|
||||||
*/
|
*/
|
||||||
protected function updateToCommit($path, $reference, $branch, $date)
|
protected function updateToCommit($path, $reference, $branch, $date)
|
||||||
{
|
{
|
||||||
$template = 'git checkout %s && git reset --hard %1$s';
|
// This uses the "--" sequence to separate branch from file parameters.
|
||||||
|
//
|
||||||
|
// Otherwise git tries the branch name as well as file name.
|
||||||
|
// If the non-existent branch is actually the name of a file, the file
|
||||||
|
// is checked out.
|
||||||
|
$template = 'git checkout %s -- && git reset --hard %1$s --';
|
||||||
$branch = preg_replace('{(?:^dev-|(?:\.x)?-dev$)}i', '', $branch);
|
$branch = preg_replace('{(?:^dev-|(?:\.x)?-dev$)}i', '', $branch);
|
||||||
|
|
||||||
$branches = null;
|
$branches = null;
|
||||||
|
@ -229,7 +237,7 @@ class GitDownloader extends VcsDownloader
|
||||||
&& $branches
|
&& $branches
|
||||||
&& preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches)
|
&& preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches)
|
||||||
) {
|
) {
|
||||||
$command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference));
|
$command = sprintf('git checkout -B %s %s -- && git reset --hard %2$s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference));
|
||||||
if (0 === $this->process->execute($command, $output, $path)) {
|
if (0 === $this->process->execute($command, $output, $path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -242,12 +250,12 @@ class GitDownloader extends VcsDownloader
|
||||||
$branch = 'v' . $branch;
|
$branch = 'v' . $branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
$command = sprintf('git checkout %s', ProcessExecutor::escape($branch));
|
$command = sprintf('git checkout %s --', ProcessExecutor::escape($branch));
|
||||||
$fallbackCommand = sprintf('git checkout -B %s %s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch));
|
$fallbackCommand = sprintf('git checkout -B %s %s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch));
|
||||||
if (0 === $this->process->execute($command, $output, $path)
|
if (0 === $this->process->execute($command, $output, $path)
|
||||||
|| 0 === $this->process->execute($fallbackCommand, $output, $path)
|
|| 0 === $this->process->execute($fallbackCommand, $output, $path)
|
||||||
) {
|
) {
|
||||||
$command = sprintf('git reset --hard %s', ProcessExecutor::escape($reference));
|
$command = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference));
|
||||||
if (0 === $this->process->execute($command, $output, $path)) {
|
if (0 === $this->process->execute($command, $output, $path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -322,6 +330,20 @@ class GitDownloader extends VcsDownloader
|
||||||
$this->hasStashedChanges = true;
|
$this->hasStashedChanges = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $path
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
protected function viewDiff($path)
|
||||||
|
{
|
||||||
|
$path = $this->normalizePath($path);
|
||||||
|
if (0 !== $this->process->execute('git diff HEAD', $output, $path)) {
|
||||||
|
throw new \RuntimeException("Could not view diff\n\n:".$this->process->getErrorOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->writeError($output);
|
||||||
|
}
|
||||||
|
|
||||||
protected function normalizePath($path)
|
protected function normalizePath($path)
|
||||||
{
|
{
|
||||||
if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) {
|
if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ class HgDownloader extends VcsDownloader
|
||||||
$this->io->writeError(" Updating to ".$target->getSourceReference());
|
$this->io->writeError(" Updating to ".$target->getSourceReference());
|
||||||
|
|
||||||
if (!is_dir($path.'/.hg')) {
|
if (!is_dir($path.'/.hg')) {
|
||||||
throw new \RuntimeException('The .hg directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
|
throw new \RuntimeException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
|
||||||
}
|
}
|
||||||
|
|
||||||
$command = sprintf('hg pull %s && hg up %s', $url, $ref);
|
$command = sprintf('hg pull %s && hg up %s', $url, $ref);
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?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\Downloader;
|
||||||
|
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a package from a local path.
|
||||||
|
*
|
||||||
|
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||||
|
* @author Johann Reinke <johann.reinke@gmail.com>
|
||||||
|
*/
|
||||||
|
class PathDownloader extends FileDownloader
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function download(PackageInterface $package, $path)
|
||||||
|
{
|
||||||
|
$fileSystem = new Filesystem();
|
||||||
|
$this->filesystem->removeDirectory($path);
|
||||||
|
|
||||||
|
$this->io->writeError(sprintf(
|
||||||
|
' - Installing <info>%s</info> (<comment>%s</comment>)',
|
||||||
|
$package->getName(),
|
||||||
|
$package->getFullPrettyVersion()
|
||||||
|
));
|
||||||
|
|
||||||
|
$url = $package->getDistUrl();
|
||||||
|
$realUrl = realpath($url);
|
||||||
|
if (false === $realUrl || !file_exists($realUrl) || !is_dir($realUrl)) {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'Path "%s" is not found',
|
||||||
|
$url
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$shortestPath = $this->filesystem->findShortestPath($path, $realUrl);
|
||||||
|
$fileSystem->symlink($shortestPath, $path);
|
||||||
|
$this->io->writeError(sprintf(' Symlinked from %s', $url));
|
||||||
|
} catch (IOException $e) {
|
||||||
|
$fileSystem->mirror($realUrl, $path);
|
||||||
|
$this->io->writeError(sprintf(' Mirrored from %s', $url));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->writeError('');
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,6 @@ class PearPackageExtractor
|
||||||
* @param array $vars used for replacement tasks
|
* @param array $vars used for replacement tasks
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
* @throws \UnexpectedValueException
|
* @throws \UnexpectedValueException
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
|
public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
|
||||||
{
|
{
|
||||||
|
@ -130,14 +129,14 @@ class PearPackageExtractor
|
||||||
* @param string $source string path to extracted files
|
* @param string $source string path to extracted files
|
||||||
* @param array $roles array [role => roleRoot] relative root for files having that role
|
* @param array $roles array [role => roleRoot] relative root for files having that role
|
||||||
* @param array $vars list of values can be used for replacement tasks
|
* @param array $vars list of values can be used for replacement tasks
|
||||||
|
* @throws \RuntimeException
|
||||||
* @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source
|
* @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source
|
||||||
* path, and target is destination of file (also relative to $source path)
|
* path, and target is destination of file (also relative to $source path)
|
||||||
* @throws \RuntimeException
|
|
||||||
*/
|
*/
|
||||||
private function buildCopyActions($source, array $roles, $vars)
|
private function buildCopyActions($source, array $roles, $vars)
|
||||||
{
|
{
|
||||||
/** @var $package \SimpleXmlElement */
|
/** @var $package \SimpleXmlElement */
|
||||||
$package = simplexml_load_file($this->combine($source, 'package.xml'));
|
$package = simplexml_load_string(file_get_contents($this->combine($source, 'package.xml')));
|
||||||
if (false === $package) {
|
if (false === $package) {
|
||||||
throw new \RuntimeException('Package definition file is not valid.');
|
throw new \RuntimeException('Package definition file is not valid.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class PerforceDownloader extends VcsDownloader
|
||||||
|
|
||||||
private function getLabelFromSourceReference($ref)
|
private function getLabelFromSourceReference($ref)
|
||||||
{
|
{
|
||||||
$pos = strpos($ref,'@');
|
$pos = strpos($ref, '@');
|
||||||
if (false !== $pos) {
|
if (false !== $pos) {
|
||||||
return substr($ref, $pos + 1);
|
return substr($ref, $pos + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class SvnDownloader extends VcsDownloader
|
||||||
$ref = $target->getSourceReference();
|
$ref = $target->getSourceReference();
|
||||||
|
|
||||||
if (!is_dir($path.'/.svn')) {
|
if (!is_dir($path.'/.svn')) {
|
||||||
throw new \RuntimeException('The .svn directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
|
throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
|
||||||
}
|
}
|
||||||
|
|
||||||
$flags = "";
|
$flags = "";
|
||||||
|
@ -151,7 +151,7 @@ class SvnDownloader extends VcsDownloader
|
||||||
*/
|
*/
|
||||||
protected function getCommitLogs($fromReference, $toReference, $path)
|
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||||
{
|
{
|
||||||
if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference) ) {
|
if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference)) {
|
||||||
// strip paths from references and only keep the actual revision
|
// strip paths from references and only keep the actual revision
|
||||||
$fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference);
|
$fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference);
|
||||||
$toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference);
|
$toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference);
|
||||||
|
|
|
@ -14,7 +14,6 @@ namespace Composer\Downloader;
|
||||||
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\Version\VersionParser;
|
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
@ -54,7 +53,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
|
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
|
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||||
$this->filesystem->emptyDirectory($path);
|
$this->filesystem->emptyDirectory($path);
|
||||||
|
|
||||||
$urls = $package->getSourceUrls();
|
$urls = $package->getSourceUrls();
|
||||||
|
@ -100,8 +99,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
}
|
}
|
||||||
$name .= ' '.$initial->getPrettyVersion();
|
$name .= ' '.$initial->getPrettyVersion();
|
||||||
} else {
|
} else {
|
||||||
$from = VersionParser::formatVersion($initial);
|
$from = $initial->getFullPrettyVersion();
|
||||||
$to = VersionParser::formatVersion($target);
|
$to = $target->getFullPrettyVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->writeError(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
|
$this->io->writeError(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
|
||||||
|
@ -146,6 +145,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
return ' ' . $line;
|
return ' ' . $line;
|
||||||
}, explode("\n", $logs)));
|
}, explode("\n", $logs)));
|
||||||
|
|
||||||
|
// escape angle brackets for proper output in the console
|
||||||
|
$logs = str_replace('<', '\<', $logs);
|
||||||
|
|
||||||
$this->io->writeError(' '.$message);
|
$this->io->writeError(' '.$message);
|
||||||
$this->io->writeError($logs);
|
$this->io->writeError($logs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ class ZipDownloader extends ArchiveDownloader
|
||||||
$zipArchive = new ZipArchive();
|
$zipArchive = new ZipArchive();
|
||||||
|
|
||||||
if (true !== ($retval = $zipArchive->open($file))) {
|
if (true !== ($retval = $zipArchive->open($file))) {
|
||||||
throw new \UnexpectedValueException($this->getErrorMessage($retval, $file), $retval);
|
throw new \UnexpectedValueException(rtrim($this->getErrorMessage($retval, $file)."\n".$processError), $retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true !== $zipArchive->extractTo($path)) {
|
if (true !== $zipArchive->extractTo($path)) {
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Event
|
||||||
protected $flags;
|
protected $flags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean Whether the event should not be passed to more listeners
|
* @var bool Whether the event should not be passed to more listeners
|
||||||
*/
|
*/
|
||||||
private $propagationStopped = false;
|
private $propagationStopped = false;
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class Event
|
||||||
/**
|
/**
|
||||||
* Checks if stopPropagation has been called
|
* Checks if stopPropagation has been called
|
||||||
*
|
*
|
||||||
* @return boolean Whether propagation has been stopped
|
* @return bool Whether propagation has been stopped
|
||||||
*/
|
*/
|
||||||
public function isPropagationStopped()
|
public function isPropagationStopped()
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ use Composer\Composer;
|
||||||
use Composer\DependencyResolver\Operation\OperationInterface;
|
use Composer\DependencyResolver\Operation\OperationInterface;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Script;
|
use Composer\Script;
|
||||||
|
use Composer\Script\CommandEvent;
|
||||||
use Composer\Script\PackageEvent;
|
use Composer\Script\PackageEvent;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
|
|
||||||
|
@ -135,10 +136,10 @@ class EventDispatcher
|
||||||
*
|
*
|
||||||
* @param Event $event The event object to pass to the event handlers/listeners.
|
* @param Event $event The event object to pass to the event handlers/listeners.
|
||||||
* @param string $additionalArgs
|
* @param string $additionalArgs
|
||||||
* @return int return code of the executed script if any, for php scripts a false return
|
|
||||||
* value is changed to 1, anything else to 0
|
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
* @return int return code of the executed script if any, for php scripts a false return
|
||||||
|
* value is changed to 1, anything else to 0
|
||||||
*/
|
*/
|
||||||
protected function doDispatch(Event $event)
|
protected function doDispatch(Event $event)
|
||||||
{
|
{
|
||||||
|
@ -170,8 +171,14 @@ class EventDispatcher
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments()));
|
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments()));
|
||||||
if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) {
|
$exec = $callable . ($args === '' ? '' : ' '.$args);
|
||||||
|
if ($this->io->isVerbose()) {
|
||||||
|
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec));
|
||||||
|
} else {
|
||||||
|
$this->io->writeError(sprintf('> %s', $exec));
|
||||||
|
}
|
||||||
|
if (0 !== ($exitCode = $this->process->execute($exec))) {
|
||||||
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
|
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
|
||||||
|
|
||||||
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
||||||
|
@ -195,12 +202,18 @@ class EventDispatcher
|
||||||
{
|
{
|
||||||
$event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
|
$event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
|
||||||
|
|
||||||
|
if ($this->io->isVerbose()) {
|
||||||
|
$this->io->writeError(sprintf('> %s: %s::%s', $event->getName(), $className, $methodName));
|
||||||
|
} else {
|
||||||
|
$this->io->writeError(sprintf('> %s::%s', $className, $methodName));
|
||||||
|
}
|
||||||
|
|
||||||
return $className::$methodName($event);
|
return $className::$methodName($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $target
|
* @param mixed $target
|
||||||
* @param Event $event
|
* @param Event $event
|
||||||
* @return Event|CommandEvent
|
* @return Event|CommandEvent
|
||||||
*/
|
*/
|
||||||
protected function checkListenerExpectedEvent($target, Event $event)
|
protected function checkListenerExpectedEvent($target, Event $event)
|
||||||
|
@ -247,7 +260,7 @@ class EventDispatcher
|
||||||
*
|
*
|
||||||
* @param string $eventName The event name - typically a constant
|
* @param string $eventName The event name - typically a constant
|
||||||
* @param Callable $listener A callable expecting an event argument
|
* @param Callable $listener A callable expecting an event argument
|
||||||
* @param integer $priority A higher value represents a higher priority
|
* @param int $priority A higher value represents a higher priority
|
||||||
*/
|
*/
|
||||||
protected function addListener($eventName, $listener, $priority = 0)
|
protected function addListener($eventName, $listener, $priority = 0)
|
||||||
{
|
{
|
||||||
|
@ -300,8 +313,8 @@ class EventDispatcher
|
||||||
/**
|
/**
|
||||||
* Checks if an event has listeners registered
|
* Checks if an event has listeners registered
|
||||||
*
|
*
|
||||||
* @param Event $event
|
* @param Event $event
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasEventListeners(Event $event)
|
public function hasEventListeners(Event $event)
|
||||||
{
|
{
|
||||||
|
@ -342,8 +355,8 @@ class EventDispatcher
|
||||||
/**
|
/**
|
||||||
* Checks if string given references a class path and method
|
* Checks if string given references a class path and method
|
||||||
*
|
*
|
||||||
* @param string $callable
|
* @param string $callable
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function isPhpScript($callable)
|
protected function isPhpScript($callable)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Composer\Config\JsonConfigSource;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Package\Archiver;
|
use Composer\Package\Archiver;
|
||||||
|
use Composer\Package\Version\VersionGuesser;
|
||||||
use Composer\Repository\RepositoryManager;
|
use Composer\Repository\RepositoryManager;
|
||||||
use Composer\Repository\WritableRepositoryInterface;
|
use Composer\Repository\WritableRepositoryInterface;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
|
@ -23,7 +24,7 @@ use Composer\Util\RemoteFilesystem;
|
||||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||||
use Composer\EventDispatcher\EventDispatcher;
|
use Composer\EventDispatcher\EventDispatcher;
|
||||||
use Composer\Autoload\AutoloadGenerator;
|
use Composer\Autoload\AutoloadGenerator;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a configured instance of composer.
|
* Creates a configured instance of composer.
|
||||||
|
@ -36,8 +37,8 @@ use Composer\Package\Version\VersionParser;
|
||||||
class Factory
|
class Factory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return string
|
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected static function getHomeDir()
|
protected static function getHomeDir()
|
||||||
{
|
{
|
||||||
|
@ -60,8 +61,7 @@ class Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $home
|
* @param string $home
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected static function getCacheDir($home)
|
protected static function getCacheDir($home)
|
||||||
|
@ -113,7 +113,7 @@ class Factory
|
||||||
$config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir)));
|
$config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir)));
|
||||||
|
|
||||||
// load global config
|
// load global config
|
||||||
$file = new JsonFile($home.'/config.json');
|
$file = new JsonFile($config->get('home').'/config.json');
|
||||||
if ($file->exists()) {
|
if ($file->exists()) {
|
||||||
if ($io && $io->isDebug()) {
|
if ($io && $io->isDebug()) {
|
||||||
$io->writeError('Loading config file ' . $file->getPath());
|
$io->writeError('Loading config file ' . $file->getPath());
|
||||||
|
@ -214,7 +214,7 @@ class Factory
|
||||||
} else {
|
} else {
|
||||||
$message = 'Composer could not find the config file: '.$localConfig;
|
$message = 'Composer could not find the config file: '.$localConfig;
|
||||||
}
|
}
|
||||||
$instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
|
$instructions = 'To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section';
|
||||||
throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
|
throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,9 +249,6 @@ class Factory
|
||||||
if ($fullLoad) {
|
if ($fullLoad) {
|
||||||
// load auth configs into the IO instance
|
// load auth configs into the IO instance
|
||||||
$io->loadConfiguration($config);
|
$io->loadConfiguration($config);
|
||||||
|
|
||||||
// setup process timeout
|
|
||||||
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize event dispatcher
|
// initialize event dispatcher
|
||||||
|
@ -267,7 +264,8 @@ class Factory
|
||||||
|
|
||||||
// load package
|
// load package
|
||||||
$parser = new VersionParser;
|
$parser = new VersionParser;
|
||||||
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io));
|
$guesser = new VersionGuesser($config, new ProcessExecutor($io), $parser);
|
||||||
|
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser);
|
||||||
$package = $loader->load($localConfig);
|
$package = $loader->load($localConfig);
|
||||||
$composer->setPackage($package);
|
$composer->setPackage($package);
|
||||||
|
|
||||||
|
@ -309,7 +307,7 @@ class Factory
|
||||||
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
|
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
|
||||||
? substr($composerFile, 0, -4).'lock'
|
? substr($composerFile, 0, -4).'lock'
|
||||||
: $composerFile . '.lock';
|
: $composerFile . '.lock';
|
||||||
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
|
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, file_get_contents($composerFile));
|
||||||
$composer->setLocker($locker);
|
$composer->setLocker($locker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +333,7 @@ class Factory
|
||||||
$rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository');
|
$rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository');
|
||||||
$rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository');
|
$rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository');
|
||||||
$rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository');
|
$rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository');
|
||||||
|
$rm->setRepositoryClass('path', 'Composer\Repository\PathRepository');
|
||||||
|
|
||||||
return $rm;
|
return $rm;
|
||||||
}
|
}
|
||||||
|
@ -407,14 +406,14 @@ class Factory
|
||||||
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
|
$dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
|
|
||||||
return $dm;
|
return $dm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Config $config The configuration
|
* @param Config $config The configuration
|
||||||
* @param Downloader\DownloadManager $dm Manager use to download sources
|
* @param Downloader\DownloadManager $dm Manager use to download sources
|
||||||
*
|
|
||||||
* @return Archiver\ArchiveManager
|
* @return Archiver\ArchiveManager
|
||||||
*/
|
*/
|
||||||
public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null)
|
public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\IO;
|
namespace Composer\IO;
|
||||||
|
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
|
||||||
abstract class BaseIO implements IOInterface
|
abstract class BaseIO implements IOInterface
|
||||||
{
|
{
|
||||||
|
@ -81,5 +82,8 @@ abstract class BaseIO implements IOInterface
|
||||||
$this->setAuthentication($domain, $cred['username'], $cred['password']);
|
$this->setAuthentication($domain, $cred['username'], $cred['password']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup process timeout
|
||||||
|
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ use Symfony\Component\Console\Helper\HelperSet;
|
||||||
class BufferIO extends ConsoleIO
|
class BufferIO extends ConsoleIO
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string $input
|
* @param string $input
|
||||||
* @param int $verbosity
|
* @param int $verbosity
|
||||||
* @param OutputFormatterInterface $formatter
|
* @param OutputFormatterInterface $formatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
|
@ -16,7 +16,8 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Helper\HelperSet;
|
use Symfony\Component\Console\Helper\HelperSet;
|
||||||
use Symfony\Component\Process\ExecutableFinder;
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Input/Output helper.
|
* The Input/Output helper.
|
||||||
|
@ -81,7 +82,7 @@ class ConsoleIO extends BaseIO
|
||||||
*/
|
*/
|
||||||
public function isVeryVerbose()
|
public function isVeryVerbose()
|
||||||
{
|
{
|
||||||
return $this->output->getVerbosity() >= 3; // OutputInterface::VERSOBITY_VERY_VERBOSE
|
return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +90,7 @@ class ConsoleIO extends BaseIO
|
||||||
*/
|
*/
|
||||||
public function isDebug()
|
public function isDebug()
|
||||||
{
|
{
|
||||||
return $this->output->getVerbosity() >= 4; // OutputInterface::VERBOSITY_DEBUG
|
return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,8 +111,8 @@ class ConsoleIO extends BaseIO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $messages
|
* @param array $messages
|
||||||
* @param boolean $newline
|
* @param bool $newline
|
||||||
* @param boolean $stderr
|
* @param bool $stderr
|
||||||
*/
|
*/
|
||||||
private function doWrite($messages, $newline, $stderr)
|
private function doWrite($messages, $newline, $stderr)
|
||||||
{
|
{
|
||||||
|
@ -126,6 +127,7 @@ class ConsoleIO extends BaseIO
|
||||||
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
|
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
|
||||||
$this->output->getErrorOutput()->write($messages, $newline);
|
$this->output->getErrorOutput()->write($messages, $newline);
|
||||||
$this->lastMessageErr = join($newline ? "\n" : '', (array) $messages);
|
$this->lastMessageErr = join($newline ? "\n" : '', (array) $messages);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,27 +153,12 @@ class ConsoleIO extends BaseIO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $messages
|
* @param array $messages
|
||||||
* @param boolean $newline
|
* @param bool $newline
|
||||||
* @param integer $size
|
* @param int $size
|
||||||
* @param boolean $stderr
|
* @param bool $stderr
|
||||||
*/
|
*/
|
||||||
private function doOverwrite($messages, $newline, $size, $stderr)
|
private function doOverwrite($messages, $newline, $size, $stderr)
|
||||||
{
|
{
|
||||||
if (true === $stderr && $this->output instanceof ConsoleOutputInterface) {
|
|
||||||
$output = $this->output->getErrorOutput();
|
|
||||||
} else {
|
|
||||||
$output = $this->output;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$output->isDecorated()) {
|
|
||||||
if (!$messages) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->doWrite($messages, count($messages) === 1 || $newline, $stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// messages can be an array, let's convert it to string anyway
|
// messages can be an array, let's convert it to string anyway
|
||||||
$messages = join($newline ? "\n" : '', (array) $messages);
|
$messages = join($newline ? "\n" : '', (array) $messages);
|
||||||
|
|
||||||
|
@ -197,7 +184,12 @@ class ConsoleIO extends BaseIO
|
||||||
if ($newline) {
|
if ($newline) {
|
||||||
$this->doWrite('', true, $stderr);
|
$this->doWrite('', true, $stderr);
|
||||||
}
|
}
|
||||||
$this->lastMessage = $messages;
|
|
||||||
|
if ($stderr) {
|
||||||
|
$this->lastMessageErr = $messages;
|
||||||
|
} else {
|
||||||
|
$this->lastMessage = $messages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,10 +203,11 @@ class ConsoleIO extends BaseIO
|
||||||
$output = $output->getErrorOutput();
|
$output = $output->getErrorOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
|
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||||
$dialog = $this->helperSet->get('dialog');
|
$helper = $this->helperSet->get('question');
|
||||||
|
$question = new Question($question, $default);
|
||||||
|
|
||||||
return $dialog->ask($output, $question, $default);
|
return $helper->ask($this->input, $output, $question);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -228,16 +221,17 @@ class ConsoleIO extends BaseIO
|
||||||
$output = $output->getErrorOutput();
|
$output = $output->getErrorOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
|
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||||
$dialog = $this->helperSet->get('dialog');
|
$helper = $this->helperSet->get('question');
|
||||||
|
$question = new ConfirmationQuestion($question, $default);
|
||||||
|
|
||||||
return $dialog->askConfirmation($output, $question, $default);
|
return $helper->ask($this->input, $output, $question);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function askAndValidate($question, $validator, $attempts = false, $default = null)
|
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||||
{
|
{
|
||||||
$output = $this->output;
|
$output = $this->output;
|
||||||
|
|
||||||
|
@ -245,10 +239,13 @@ class ConsoleIO extends BaseIO
|
||||||
$output = $output->getErrorOutput();
|
$output = $output->getErrorOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var \Symfony\Component\Console\Helper\DialogHelper $dialog */
|
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||||
$dialog = $this->helperSet->get('dialog');
|
$helper = $this->helperSet->get('question');
|
||||||
|
$question = new Question($question, $default);
|
||||||
|
$question->setValidator($validator);
|
||||||
|
$question->setMaxAttempts($attempts);
|
||||||
|
|
||||||
return $dialog->askAndValidate($output, $question, $validator, $attempts, $default);
|
return $helper->ask($this->input, $output, $question);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,71 +253,8 @@ class ConsoleIO extends BaseIO
|
||||||
*/
|
*/
|
||||||
public function askAndHideAnswer($question)
|
public function askAndHideAnswer($question)
|
||||||
{
|
{
|
||||||
// handle windows
|
$this->writeError($question, false);
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
|
||||||
$finder = new ExecutableFinder();
|
|
||||||
|
|
||||||
// use bash if it's present
|
return \Seld\CliPrompt\CliPrompt::hiddenPrompt(true);
|
||||||
if ($finder->find('bash') && $finder->find('stty')) {
|
|
||||||
$this->writeError($question, false);
|
|
||||||
$value = rtrim(shell_exec('bash -c "stty -echo; read -n0 discard; read -r mypassword; stty echo; echo $mypassword"'));
|
|
||||||
$this->writeError('');
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback to hiddeninput executable
|
|
||||||
$exe = __DIR__.'\\hiddeninput.exe';
|
|
||||||
|
|
||||||
// handle code running from a phar
|
|
||||||
if ('phar:' === substr(__FILE__, 0, 5)) {
|
|
||||||
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
|
|
||||||
|
|
||||||
// use stream_copy_to_stream instead of copy
|
|
||||||
// to work around https://bugs.php.net/bug.php?id=64634
|
|
||||||
$source = fopen(__DIR__.'\\hiddeninput.exe', 'r');
|
|
||||||
$target = fopen($tmpExe, 'w+');
|
|
||||||
stream_copy_to_stream($source, $target);
|
|
||||||
fclose($source);
|
|
||||||
fclose($target);
|
|
||||||
unset($source, $target);
|
|
||||||
|
|
||||||
$exe = $tmpExe;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->writeError($question, false);
|
|
||||||
$value = rtrim(shell_exec($exe));
|
|
||||||
$this->writeError('');
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
if (isset($tmpExe)) {
|
|
||||||
unlink($tmpExe);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists('/usr/bin/env')) {
|
|
||||||
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
|
|
||||||
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
|
|
||||||
foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
|
|
||||||
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
|
|
||||||
$shell = $sh;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($shell)) {
|
|
||||||
$this->writeError($question, false);
|
|
||||||
$readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword';
|
|
||||||
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
|
|
||||||
$value = rtrim(shell_exec($command));
|
|
||||||
$this->writeError('');
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not able to hide the answer, proceed with normal question handling
|
|
||||||
return $this->ask($question);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ interface IOInterface
|
||||||
*
|
*
|
||||||
* @param string|array $messages The message as an array of lines or a single string
|
* @param string|array $messages The message as an array of lines or a single string
|
||||||
* @param bool $newline Whether to add a newline or not
|
* @param bool $newline Whether to add a newline or not
|
||||||
* @param integer $size The size of line
|
* @param int $size The size of line
|
||||||
*/
|
*/
|
||||||
public function overwrite($messages, $newline = true, $size = null);
|
public function overwrite($messages, $newline = true, $size = null);
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ interface IOInterface
|
||||||
*
|
*
|
||||||
* @param string|array $messages The message as an array of lines or a single string
|
* @param string|array $messages The message as an array of lines or a single string
|
||||||
* @param bool $newline Whether to add a newline or not
|
* @param bool $newline Whether to add a newline or not
|
||||||
* @param integer $size The size of line
|
* @param int $size The size of line
|
||||||
*/
|
*/
|
||||||
public function overwriteError($messages, $newline = true, $size = null);
|
public function overwriteError($messages, $newline = true, $size = null);
|
||||||
|
|
||||||
|
@ -96,9 +96,8 @@ interface IOInterface
|
||||||
* @param string|array $question The question to ask
|
* @param string|array $question The question to ask
|
||||||
* @param string $default The default answer if none is given by the user
|
* @param string $default The default answer if none is given by the user
|
||||||
*
|
*
|
||||||
* @return string The user answer
|
|
||||||
*
|
|
||||||
* @throws \RuntimeException If there is no data to read in the input stream
|
* @throws \RuntimeException If there is no data to read in the input stream
|
||||||
|
* @return string The user answer
|
||||||
*/
|
*/
|
||||||
public function ask($question, $default = null);
|
public function ask($question, $default = null);
|
||||||
|
|
||||||
|
@ -123,14 +122,13 @@ interface IOInterface
|
||||||
*
|
*
|
||||||
* @param string|array $question The question to ask
|
* @param string|array $question The question to ask
|
||||||
* @param callback $validator A PHP callback
|
* @param callback $validator A PHP callback
|
||||||
* @param bool|integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
|
* @param null|int $attempts Max number of times to ask before giving up (default of null means infinite)
|
||||||
* @param string $default The default answer if none is given by the user
|
* @param string $default The default answer if none is given by the user
|
||||||
*
|
*
|
||||||
* @return mixed
|
|
||||||
*
|
|
||||||
* @throws \Exception When any of the validators return an error
|
* @throws \Exception When any of the validators return an error
|
||||||
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function askAndValidate($question, $validator, $attempts = false, $default = null);
|
public function askAndValidate($question, $validator, $attempts = null, $default = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks a question to the user and hide the answer.
|
* Asks a question to the user and hide the answer.
|
||||||
|
@ -153,7 +151,7 @@ interface IOInterface
|
||||||
*
|
*
|
||||||
* @param string $repositoryName The unique name of repository
|
* @param string $repositoryName The unique name of repository
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasAuthentication($repositoryName);
|
public function hasAuthentication($repositoryName);
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -18,6 +18,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||||
use Composer\DependencyResolver\Operation\OperationInterface;
|
use Composer\DependencyResolver\Operation\OperationInterface;
|
||||||
|
use Composer\DependencyResolver\PolicyInterface;
|
||||||
use Composer\DependencyResolver\Pool;
|
use Composer\DependencyResolver\Pool;
|
||||||
use Composer\DependencyResolver\Request;
|
use Composer\DependencyResolver\Request;
|
||||||
use Composer\DependencyResolver\Rule;
|
use Composer\DependencyResolver\Rule;
|
||||||
|
@ -33,7 +34,7 @@ use Composer\Json\JsonFile;
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\CompletePackage;
|
use Composer\Package\CompletePackage;
|
||||||
use Composer\Package\Link;
|
use Composer\Package\Link;
|
||||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
use Composer\Semver\Constraint\Constraint;
|
||||||
use Composer\Package\Locker;
|
use Composer\Package\Locker;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\RootPackageInterface;
|
use Composer\Package\RootPackageInterface;
|
||||||
|
@ -43,6 +44,7 @@ use Composer\Repository\InstalledFilesystemRepository;
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
use Composer\Repository\RepositoryManager;
|
use Composer\Repository\RepositoryManager;
|
||||||
|
use Composer\Repository\WritableRepositoryInterface;
|
||||||
use Composer\Script\ScriptEvents;
|
use Composer\Script\ScriptEvents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,6 +103,7 @@ class Installer
|
||||||
protected $preferSource = false;
|
protected $preferSource = false;
|
||||||
protected $preferDist = false;
|
protected $preferDist = false;
|
||||||
protected $optimizeAutoloader = false;
|
protected $optimizeAutoloader = false;
|
||||||
|
protected $classMapAuthoritative = false;
|
||||||
protected $devMode = false;
|
protected $devMode = false;
|
||||||
protected $dryRun = false;
|
protected $dryRun = false;
|
||||||
protected $verbose = false;
|
protected $verbose = false;
|
||||||
|
@ -157,9 +160,8 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* Run installation (or update)
|
* Run installation (or update)
|
||||||
*
|
*
|
||||||
* @return int 0 on success or a positive error code on failure
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
* @return int 0 on success or a positive error code on failure
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
@ -206,7 +208,12 @@ class Installer
|
||||||
|
|
||||||
// create installed repo, this contains all local packages + platform packages (php & extensions)
|
// create installed repo, this contains all local packages + platform packages (php & extensions)
|
||||||
$localRepo = $this->repositoryManager->getLocalRepository();
|
$localRepo = $this->repositoryManager->getLocalRepository();
|
||||||
$platformRepo = new PlatformRepository();
|
if (!$this->update && $this->locker->isLocked()) {
|
||||||
|
$platformOverrides = $this->locker->getPlatformOverrides();
|
||||||
|
} else {
|
||||||
|
$platformOverrides = $this->config->get('platform') ?: array();
|
||||||
|
}
|
||||||
|
$platformRepo = new PlatformRepository(array(), $platformOverrides);
|
||||||
$repos = array(
|
$repos = array(
|
||||||
$localRepo,
|
$localRepo,
|
||||||
new InstalledArrayRepository(array($installedRootPackage)),
|
new InstalledArrayRepository(array($installedRootPackage)),
|
||||||
|
@ -227,11 +234,15 @@ class Installer
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->installationManager->notifyInstalls();
|
if (!$this->dryRun) {
|
||||||
|
$this->installationManager->notifyInstalls($this->io);
|
||||||
|
}
|
||||||
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
$this->installationManager->notifyInstalls();
|
if (!$this->dryRun) {
|
||||||
|
$this->installationManager->notifyInstalls($this->io);
|
||||||
|
}
|
||||||
|
|
||||||
// output suggestions if we're in dev mode
|
// output suggestions if we're in dev mode
|
||||||
if ($this->devMode) {
|
if ($this->devMode) {
|
||||||
|
@ -259,7 +270,7 @@ class Installer
|
||||||
|
|
||||||
$this->io->writeError(
|
$this->io->writeError(
|
||||||
sprintf(
|
sprintf(
|
||||||
"<error>Package %s is abandoned, you should avoid using it. %s.</error>",
|
"<warning>Package %s is abandoned, you should avoid using it. %s.</warning>",
|
||||||
$package->getPrettyName(),
|
$package->getPrettyName(),
|
||||||
$replacement
|
$replacement
|
||||||
)
|
)
|
||||||
|
@ -282,7 +293,7 @@ class Installer
|
||||||
$pool->addRepository($installedRepo, $aliases);
|
$pool->addRepository($installedRepo, $aliases);
|
||||||
|
|
||||||
// creating requirements request
|
// creating requirements request
|
||||||
$request = $this->createRequest($pool, $this->package, $platformRepo);
|
$request = $this->createRequest($this->package, $platformRepo);
|
||||||
$request->updateAll();
|
$request->updateAll();
|
||||||
foreach ($this->package->getRequires() as $link) {
|
foreach ($this->package->getRequires() as $link) {
|
||||||
$request->install($link->getTarget(), $link->getConstraint());
|
$request->install($link->getTarget(), $link->getConstraint());
|
||||||
|
@ -311,7 +322,8 @@ class Installer
|
||||||
$this->package->getMinimumStability(),
|
$this->package->getMinimumStability(),
|
||||||
$this->package->getStabilityFlags(),
|
$this->package->getStabilityFlags(),
|
||||||
$this->preferStable || $this->package->getPreferStable(),
|
$this->preferStable || $this->package->getPreferStable(),
|
||||||
$this->preferLowest
|
$this->preferLowest,
|
||||||
|
$this->config->get('platform') ?: array()
|
||||||
);
|
);
|
||||||
if ($updatedLock) {
|
if ($updatedLock) {
|
||||||
$this->io->writeError('<info>Writing lock file</info>');
|
$this->io->writeError('<info>Writing lock file</info>');
|
||||||
|
@ -327,6 +339,7 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->autoloadGenerator->setDevMode($this->devMode);
|
$this->autoloadGenerator->setDevMode($this->devMode);
|
||||||
|
$this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative);
|
||||||
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
|
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,13 +351,23 @@ class Installer
|
||||||
|
|
||||||
$vendorDir = $this->config->get('vendor-dir');
|
$vendorDir = $this->config->get('vendor-dir');
|
||||||
if (is_dir($vendorDir)) {
|
if (is_dir($vendorDir)) {
|
||||||
touch($vendorDir);
|
// suppress errors as this fails sometimes on OSX for no apparent reason
|
||||||
|
// see https://github.com/composer/composer/issues/4070#issuecomment-129792748
|
||||||
|
@touch($vendorDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RepositoryInterface $localRepo
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param PlatformRepository $platformRepo
|
||||||
|
* @param array $aliases
|
||||||
|
* @param bool $withDevReqs
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs)
|
protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs)
|
||||||
{
|
{
|
||||||
// init vars
|
// init vars
|
||||||
|
@ -352,9 +375,12 @@ class Installer
|
||||||
$repositories = null;
|
$repositories = null;
|
||||||
|
|
||||||
// initialize locker to create aliased packages
|
// initialize locker to create aliased packages
|
||||||
$installFromLock = false;
|
$installFromLock = !$this->update && $this->locker->isLocked();
|
||||||
if (!$this->update && $this->locker->isLocked()) {
|
|
||||||
$installFromLock = true;
|
// initialize locked repo if we are installing from lock or in a partial update
|
||||||
|
// and a lock file is present as we need to force install non-whitelisted lock file
|
||||||
|
// packages in that case
|
||||||
|
if ($installFromLock || (!empty($this->updateWhitelist) && $this->locker->isLocked())) {
|
||||||
try {
|
try {
|
||||||
$lockedRepository = $this->locker->getLockedRepository($withDevReqs);
|
$lockedRepository = $this->locker->getLockedRepository($withDevReqs);
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
|
@ -378,21 +404,23 @@ class Installer
|
||||||
|
|
||||||
// creating repository pool
|
// creating repository pool
|
||||||
$policy = $this->createPolicy();
|
$policy = $this->createPolicy();
|
||||||
$pool = $this->createPool($withDevReqs, $lockedRepository);
|
$pool = $this->createPool($withDevReqs, $installFromLock ? $lockedRepository : null);
|
||||||
$pool->addRepository($installedRepo, $aliases);
|
$pool->addRepository($installedRepo, $aliases);
|
||||||
if ($installFromLock) {
|
|
||||||
$pool->addRepository($lockedRepository, $aliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$installFromLock) {
|
if (!$installFromLock) {
|
||||||
$repositories = $this->repositoryManager->getRepositories();
|
$repositories = $this->repositoryManager->getRepositories();
|
||||||
foreach ($repositories as $repository) {
|
foreach ($repositories as $repository) {
|
||||||
$pool->addRepository($repository, $aliases);
|
$pool->addRepository($repository, $aliases);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add the locked repository after the others in case we are doing a
|
||||||
|
// partial update so missing packages can be found there still.
|
||||||
|
// For installs from lock it's the only one added so it is first
|
||||||
|
if ($lockedRepository) {
|
||||||
|
$pool->addRepository($lockedRepository, $aliases);
|
||||||
|
}
|
||||||
|
|
||||||
// creating requirements request
|
// creating requirements request
|
||||||
$request = $this->createRequest($pool, $this->package, $platformRepo);
|
$request = $this->createRequest($this->package, $platformRepo);
|
||||||
|
|
||||||
if (!$installFromLock) {
|
if (!$installFromLock) {
|
||||||
// remove unstable packages from the localRepo if they don't match the current stability settings
|
// remove unstable packages from the localRepo if they don't match the current stability settings
|
||||||
|
@ -403,7 +431,7 @@ class Installer
|
||||||
&& $this->installationManager->isPackageInstalled($localRepo, $package)
|
&& $this->installationManager->isPackageInstalled($localRepo, $package)
|
||||||
) {
|
) {
|
||||||
$removedUnstablePackages[$package->getName()] = true;
|
$removedUnstablePackages[$package->getName()] = true;
|
||||||
$request->remove($package->getName(), new VersionConstraint('=', $package->getVersion()));
|
$request->remove($package->getName(), new Constraint('=', $package->getVersion()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,16 +454,7 @@ class Installer
|
||||||
// if the updateWhitelist is enabled, packages not in it are also fixed
|
// if the updateWhitelist is enabled, packages not in it are also fixed
|
||||||
// to the version specified in the lock, or their currently installed version
|
// to the version specified in the lock, or their currently installed version
|
||||||
if ($this->updateWhitelist) {
|
if ($this->updateWhitelist) {
|
||||||
if ($this->locker->isLocked()) {
|
$currentPackages = $this->getCurrentPackages($withDevReqs, $installedRepo);
|
||||||
try {
|
|
||||||
$currentPackages = $this->locker->getLockedRepository($withDevReqs)->getPackages();
|
|
||||||
} catch (\RuntimeException $e) {
|
|
||||||
// fetch only non-dev packages from lock if doing a dev update fails due to a previously incomplete lock file
|
|
||||||
$currentPackages = $this->locker->getLockedRepository()->getPackages();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$currentPackages = $installedRepo->getPackages();
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect packages to fixate from root requirements as well as installed packages
|
// collect packages to fixate from root requirements as well as installed packages
|
||||||
$candidates = array();
|
$candidates = array();
|
||||||
|
@ -451,7 +470,7 @@ class Installer
|
||||||
foreach ($currentPackages as $curPackage) {
|
foreach ($currentPackages as $curPackage) {
|
||||||
if ($curPackage->getName() === $candidate) {
|
if ($curPackage->getName() === $candidate) {
|
||||||
if (!$this->isUpdateable($curPackage) && !isset($removedUnstablePackages[$curPackage->getName()])) {
|
if (!$this->isUpdateable($curPackage) && !isset($removedUnstablePackages[$curPackage->getName()])) {
|
||||||
$constraint = new VersionConstraint('=', $curPackage->getVersion());
|
$constraint = new Constraint('=', $curPackage->getVersion());
|
||||||
$request->install($curPackage->getName(), $constraint);
|
$request->install($curPackage->getName(), $constraint);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -471,7 +490,7 @@ class Installer
|
||||||
if (isset($aliases[$package->getName()][$version])) {
|
if (isset($aliases[$package->getName()][$version])) {
|
||||||
$version = $aliases[$package->getName()][$version]['alias_normalized'];
|
$version = $aliases[$package->getName()][$version]['alias_normalized'];
|
||||||
}
|
}
|
||||||
$constraint = new VersionConstraint('=', $version);
|
$constraint = new Constraint('=', $version);
|
||||||
$constraint->setPrettyString($package->getPrettyVersion());
|
$constraint->setPrettyString($package->getPrettyVersion());
|
||||||
$request->install($package->getName(), $constraint);
|
$request->install($package->getName(), $constraint);
|
||||||
}
|
}
|
||||||
|
@ -494,7 +513,7 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
// force dev packages to have the latest links if we update or install from a (potentially new) lock
|
// force dev packages to have the latest links if we update or install from a (potentially new) lock
|
||||||
$this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links');
|
$this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, 'force-links');
|
||||||
|
|
||||||
// solve dependencies
|
// solve dependencies
|
||||||
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request);
|
$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request);
|
||||||
|
@ -509,8 +528,13 @@ class Installer
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->io->isVerbose()) {
|
||||||
|
$this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies");
|
||||||
|
$this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies");
|
||||||
|
}
|
||||||
|
|
||||||
// force dev packages to be updated if we update or install from a (potentially new) lock
|
// force dev packages to be updated if we update or install from a (potentially new) lock
|
||||||
$operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-updates', $operations);
|
$operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, 'force-updates', $operations);
|
||||||
|
|
||||||
// execute operations
|
// execute operations
|
||||||
if (!$operations) {
|
if (!$operations) {
|
||||||
|
@ -550,7 +574,8 @@ class Installer
|
||||||
if ('update' === $operation->getJobType()
|
if ('update' === $operation->getJobType()
|
||||||
&& $operation->getTargetPackage()->isDev()
|
&& $operation->getTargetPackage()->isDev()
|
||||||
&& $operation->getTargetPackage()->getVersion() === $operation->getInitialPackage()->getVersion()
|
&& $operation->getTargetPackage()->getVersion() === $operation->getInitialPackage()->getVersion()
|
||||||
&& $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference()
|
&& (!$operation->getTargetPackage()->getSourceReference() || $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference())
|
||||||
|
&& (!$operation->getTargetPackage()->getDistReference() || $operation->getTargetPackage()->getDistReference() === $operation->getInitialPackage()->getDistReference())
|
||||||
) {
|
) {
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
|
$this->io->writeError(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
|
||||||
|
@ -604,6 +629,12 @@ class Installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->dryRun) {
|
||||||
|
// force source/dist urls to be updated for all packages
|
||||||
|
$this->processPackageUrls($pool, $policy, $localRepo, $repositories);
|
||||||
|
$localRepo->write();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,6 +702,11 @@ class Installer
|
||||||
return array_merge($uninstOps, $operations);
|
return array_merge($uninstOps, $operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $withDevReqs
|
||||||
|
* @param RepositoryInterface|null $lockedRepository
|
||||||
|
* @return Pool
|
||||||
|
*/
|
||||||
private function createPool($withDevReqs, RepositoryInterface $lockedRepository = null)
|
private function createPool($withDevReqs, RepositoryInterface $lockedRepository = null)
|
||||||
{
|
{
|
||||||
if (!$this->update && $this->locker->isLocked()) { // install from lock
|
if (!$this->update && $this->locker->isLocked()) { // install from lock
|
||||||
|
@ -679,7 +715,7 @@ class Installer
|
||||||
|
|
||||||
$requires = array();
|
$requires = array();
|
||||||
foreach ($lockedRepository->getPackages() as $package) {
|
foreach ($lockedRepository->getPackages() as $package) {
|
||||||
$constraint = new VersionConstraint('=', $package->getVersion());
|
$constraint = new Constraint('=', $package->getVersion());
|
||||||
$constraint->setPrettyString($package->getPrettyVersion());
|
$constraint->setPrettyString($package->getPrettyVersion());
|
||||||
$requires[$package->getName()] = $constraint;
|
$requires[$package->getName()] = $constraint;
|
||||||
}
|
}
|
||||||
|
@ -709,6 +745,9 @@ class Installer
|
||||||
return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
|
return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DefaultPolicy
|
||||||
|
*/
|
||||||
private function createPolicy()
|
private function createPolicy()
|
||||||
{
|
{
|
||||||
$preferStable = null;
|
$preferStable = null;
|
||||||
|
@ -729,11 +768,16 @@ class Installer
|
||||||
return new DefaultPolicy($preferStable, $preferLowest);
|
return new DefaultPolicy($preferStable, $preferLowest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo)
|
/**
|
||||||
|
* @param RootPackageInterface $rootPackage
|
||||||
|
* @param PlatformRepository $platformRepo
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
private function createRequest(RootPackageInterface $rootPackage, PlatformRepository $platformRepo)
|
||||||
{
|
{
|
||||||
$request = new Request($pool);
|
$request = new Request();
|
||||||
|
|
||||||
$constraint = new VersionConstraint('=', $rootPackage->getVersion());
|
$constraint = new Constraint('=', $rootPackage->getVersion());
|
||||||
$constraint->setPrettyString($rootPackage->getPrettyVersion());
|
$constraint->setPrettyString($rootPackage->getPrettyVersion());
|
||||||
$request->install($rootPackage->getName(), $constraint);
|
$request->install($rootPackage->getName(), $constraint);
|
||||||
|
|
||||||
|
@ -747,7 +791,7 @@ class Installer
|
||||||
// to prevent the solver trying to remove or update those
|
// to prevent the solver trying to remove or update those
|
||||||
$provided = $rootPackage->getProvides();
|
$provided = $rootPackage->getProvides();
|
||||||
foreach ($fixedPackages as $package) {
|
foreach ($fixedPackages as $package) {
|
||||||
$constraint = new VersionConstraint('=', $package->getVersion());
|
$constraint = new Constraint('=', $package->getVersion());
|
||||||
$constraint->setPrettyString($package->getPrettyVersion());
|
$constraint->setPrettyString($package->getPrettyVersion());
|
||||||
|
|
||||||
// skip platform packages that are provided by the root package
|
// skip platform packages that are provided by the root package
|
||||||
|
@ -762,7 +806,20 @@ class Installer
|
||||||
return $request;
|
return $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, $task, array $operations = null)
|
/**
|
||||||
|
* @param WritableRepositoryInterface $localRepo
|
||||||
|
* @param Pool $pool
|
||||||
|
* @param PolicyInterface $policy
|
||||||
|
* @param array $repositories
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param RepositoryInterface $lockedRepository
|
||||||
|
* @param bool $installFromLock
|
||||||
|
* @param bool $withDevReqs
|
||||||
|
* @param string $task
|
||||||
|
* @param array|null $operations
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $installFromLock, $withDevReqs, $task, array $operations = null)
|
||||||
{
|
{
|
||||||
if ($task === 'force-updates' && null === $operations) {
|
if ($task === 'force-updates' && null === $operations) {
|
||||||
throw new \InvalidArgumentException('Missing operations argument');
|
throw new \InvalidArgumentException('Missing operations argument');
|
||||||
|
@ -771,6 +828,10 @@ class Installer
|
||||||
$operations = array();
|
$operations = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$installFromLock && $this->updateWhitelist) {
|
||||||
|
$currentPackages = $this->getCurrentPackages($withDevReqs, $installedRepo);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($localRepo->getCanonicalPackages() as $package) {
|
foreach ($localRepo->getCanonicalPackages() as $package) {
|
||||||
// skip non-dev packages
|
// skip non-dev packages
|
||||||
if (!$package->isDev()) {
|
if (!$package->isDev()) {
|
||||||
|
@ -811,11 +872,31 @@ class Installer
|
||||||
if ($this->update) {
|
if ($this->update) {
|
||||||
// skip package if the whitelist is enabled and it is not in it
|
// skip package if the whitelist is enabled and it is not in it
|
||||||
if ($this->updateWhitelist && !$this->isUpdateable($package)) {
|
if ($this->updateWhitelist && !$this->isUpdateable($package)) {
|
||||||
|
// check if non-updateable packages are out of date compared to the lock file to ensure we don't corrupt it
|
||||||
|
foreach ($currentPackages as $curPackage) {
|
||||||
|
if ($curPackage->isDev() && $curPackage->getName() === $package->getName() && $curPackage->getVersion() === $package->getVersion()) {
|
||||||
|
if ($task === 'force-links') {
|
||||||
|
$package->setRequires($curPackage->getRequires());
|
||||||
|
$package->setConflicts($curPackage->getConflicts());
|
||||||
|
$package->setProvides($curPackage->getProvides());
|
||||||
|
$package->setReplaces($curPackage->getReplaces());
|
||||||
|
} elseif ($task === 'force-updates') {
|
||||||
|
if (($curPackage->getSourceReference() && $curPackage->getSourceReference() !== $package->getSourceReference())
|
||||||
|
|| ($curPackage->getDistReference() && $curPackage->getDistReference() !== $package->getDistReference())
|
||||||
|
) {
|
||||||
|
$operations[] = new UpdateOperation($package, $curPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find similar packages (name/version) in all repositories
|
// find similar packages (name/version) in all repositories
|
||||||
$matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion()));
|
$matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
|
||||||
foreach ($matches as $index => $match) {
|
foreach ($matches as $index => $match) {
|
||||||
// skip local packages
|
// skip local packages
|
||||||
if (!in_array($match->getRepository(), $repositories, true)) {
|
if (!in_array($match->getRepository(), $repositories, true)) {
|
||||||
|
@ -832,8 +913,8 @@ class Installer
|
||||||
$matches[$index] = $match->getId();
|
$matches[$index] = $match->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// select prefered package according to policy rules
|
// select preferred package according to policy rules
|
||||||
if ($matches && $matches = $policy->selectPreferedPackages($pool, array(), $matches)) {
|
if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) {
|
||||||
$newPackage = $pool->literalToPackage($matches[0]);
|
$newPackage = $pool->literalToPackage($matches[0]);
|
||||||
|
|
||||||
if ($task === 'force-links' && $newPackage) {
|
if ($task === 'force-links' && $newPackage) {
|
||||||
|
@ -854,7 +935,7 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($task === 'force-updates') {
|
if ($task === 'force-updates') {
|
||||||
// force installed package to update to referenced version if it does not match the installed version
|
// force installed package to update to referenced version in root package if it does not match the installed version
|
||||||
$references = $this->package->getReferences();
|
$references = $this->package->getReferences();
|
||||||
|
|
||||||
if (isset($references[$package->getName()]) && $references[$package->getName()] !== $package->getSourceReference()) {
|
if (isset($references[$package->getName()]) && $references[$package->getName()] !== $package->getSourceReference()) {
|
||||||
|
@ -868,6 +949,29 @@ class Installer
|
||||||
return $operations;
|
return $operations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the most "current" list of packages that are installed meaning from lock ideally or from installed repo as fallback
|
||||||
|
* @param bool $withDevReqs
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getCurrentPackages($withDevReqs, $installedRepo)
|
||||||
|
{
|
||||||
|
if ($this->locker->isLocked()) {
|
||||||
|
try {
|
||||||
|
return $this->locker->getLockedRepository($withDevReqs)->getPackages();
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
// fetch only non-dev packages from lock if doing a dev update fails due to a previously incomplete lock file
|
||||||
|
return $this->locker->getLockedRepository()->getPackages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installedRepo->getPackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
private function getRootAliases()
|
private function getRootAliases()
|
||||||
{
|
{
|
||||||
if (!$this->update && $this->locker->isLocked()) {
|
if (!$this->update && $this->locker->isLocked()) {
|
||||||
|
@ -881,13 +985,71 @@ class Installer
|
||||||
foreach ($aliases as $alias) {
|
foreach ($aliases as $alias) {
|
||||||
$normalizedAliases[$alias['package']][$alias['version']] = array(
|
$normalizedAliases[$alias['package']][$alias['version']] = array(
|
||||||
'alias' => $alias['alias'],
|
'alias' => $alias['alias'],
|
||||||
'alias_normalized' => $alias['alias_normalized']
|
'alias_normalized' => $alias['alias_normalized'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $normalizedAliases;
|
return $normalizedAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Pool $pool
|
||||||
|
* @param PolicyInterface $policy
|
||||||
|
* @param WritableRepositoryInterface $localRepo
|
||||||
|
* @param array $repositories
|
||||||
|
*/
|
||||||
|
private function processPackageUrls($pool, $policy, $localRepo, $repositories)
|
||||||
|
{
|
||||||
|
if (!$this->update) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($localRepo->getCanonicalPackages() as $package) {
|
||||||
|
// find similar packages (name/version) in all repositories
|
||||||
|
$matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
|
||||||
|
foreach ($matches as $index => $match) {
|
||||||
|
// skip local packages
|
||||||
|
if (!in_array($match->getRepository(), $repositories, true)) {
|
||||||
|
unset($matches[$index]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip providers/replacers
|
||||||
|
if ($match->getName() !== $package->getName()) {
|
||||||
|
unset($matches[$index]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches[$index] = $match->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// select preferred package according to policy rules
|
||||||
|
if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) {
|
||||||
|
$newPackage = $pool->literalToPackage($matches[0]);
|
||||||
|
|
||||||
|
// update the dist and source URLs
|
||||||
|
$sourceUrl = $package->getSourceUrl();
|
||||||
|
$newSourceUrl = $newPackage->getSourceUrl();
|
||||||
|
|
||||||
|
if ($sourceUrl !== $newSourceUrl) {
|
||||||
|
$package->setSourceType($newPackage->getSourceType());
|
||||||
|
$package->setSourceUrl($newSourceUrl);
|
||||||
|
$package->setSourceReference($newPackage->getSourceReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
// only update dist url for github/bitbucket dists as they use a combination of dist url + dist reference to install
|
||||||
|
// but for other urls this is ambiguous and could result in bad outcomes
|
||||||
|
if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com)/}', $newPackage->getDistUrl())) {
|
||||||
|
$package->setDistUrl($newPackage->getDistUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PlatformRepository $platformRepo
|
||||||
|
* @param array $aliases
|
||||||
|
*/
|
||||||
private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases)
|
private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases)
|
||||||
{
|
{
|
||||||
foreach ($aliases as $package => $versions) {
|
foreach ($aliases as $package => $versions) {
|
||||||
|
@ -902,6 +1064,10 @@ class Installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PackageInterface $package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
private function isUpdateable(PackageInterface $package)
|
private function isUpdateable(PackageInterface $package)
|
||||||
{
|
{
|
||||||
if (!$this->updateWhitelist) {
|
if (!$this->updateWhitelist) {
|
||||||
|
@ -931,6 +1097,10 @@ class Installer
|
||||||
return "{^" . $cleanedWhiteListedPattern . "$}i";
|
return "{^" . $cleanedWhiteListedPattern . "$}i";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $links
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
private function extractPlatformRequirements($links)
|
private function extractPlatformRequirements($links)
|
||||||
{
|
{
|
||||||
$platformReqs = array();
|
$platformReqs = array();
|
||||||
|
@ -951,7 +1121,7 @@ class Installer
|
||||||
* update whitelist themselves.
|
* update whitelist themselves.
|
||||||
*
|
*
|
||||||
* @param RepositoryInterface $localRepo
|
* @param RepositoryInterface $localRepo
|
||||||
* @param boolean $devMode
|
* @param bool $devMode
|
||||||
* @param array $rootRequires An array of links to packages in require of the root package
|
* @param array $rootRequires An array of links to packages in require of the root package
|
||||||
* @param array $rootDevRequires An array of links to packages in require-dev of the root package
|
* @param array $rootDevRequires An array of links to packages in require-dev of the root package
|
||||||
*/
|
*/
|
||||||
|
@ -1083,6 +1253,10 @@ class Installer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RepositoryInterface $additionalInstalledRepository
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository)
|
public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository)
|
||||||
{
|
{
|
||||||
$this->additionalInstalledRepository = $additionalInstalledRepository;
|
$this->additionalInstalledRepository = $additionalInstalledRepository;
|
||||||
|
@ -1093,7 +1267,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* Whether to run in drymode or not
|
* Whether to run in drymode or not
|
||||||
*
|
*
|
||||||
* @param boolean $dryRun
|
* @param bool $dryRun
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setDryRun($dryRun = true)
|
public function setDryRun($dryRun = true)
|
||||||
|
@ -1116,7 +1290,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* prefer source installation
|
* prefer source installation
|
||||||
*
|
*
|
||||||
* @param boolean $preferSource
|
* @param bool $preferSource
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setPreferSource($preferSource = true)
|
public function setPreferSource($preferSource = true)
|
||||||
|
@ -1129,7 +1303,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* prefer dist installation
|
* prefer dist installation
|
||||||
*
|
*
|
||||||
* @param boolean $preferDist
|
* @param bool $preferDist
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setPreferDist($preferDist = true)
|
public function setPreferDist($preferDist = true)
|
||||||
|
@ -1148,6 +1322,29 @@ class Installer
|
||||||
public function setOptimizeAutoloader($optimizeAutoloader = false)
|
public function setOptimizeAutoloader($optimizeAutoloader = false)
|
||||||
{
|
{
|
||||||
$this->optimizeAutoloader = (boolean) $optimizeAutoloader;
|
$this->optimizeAutoloader = (boolean) $optimizeAutoloader;
|
||||||
|
if (!$this->optimizeAutoloader) {
|
||||||
|
// Force classMapAuthoritative off when not optimizing the
|
||||||
|
// autoloader
|
||||||
|
$this->setClassMapAuthoritative(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not generated autoloader considers the class map
|
||||||
|
* authoritative.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
* @return Installer
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative = false)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = (boolean) $classMapAuthoritative;
|
||||||
|
if ($this->classMapAuthoritative) {
|
||||||
|
// Force optimizeAutoloader when classmap is authoritative
|
||||||
|
$this->setOptimizeAutoloader(true);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -1155,7 +1352,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* update packages
|
* update packages
|
||||||
*
|
*
|
||||||
* @param boolean $update
|
* @param bool $update
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setUpdate($update = true)
|
public function setUpdate($update = true)
|
||||||
|
@ -1168,7 +1365,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* enables dev packages
|
* enables dev packages
|
||||||
*
|
*
|
||||||
* @param boolean $devMode
|
* @param bool $devMode
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setDevMode($devMode = true)
|
public function setDevMode($devMode = true)
|
||||||
|
@ -1181,7 +1378,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* set whether to run autoloader or not
|
* set whether to run autoloader or not
|
||||||
*
|
*
|
||||||
* @param boolean $dumpAutoloader
|
* @param bool $dumpAutoloader
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setDumpAutoloader($dumpAutoloader = true)
|
public function setDumpAutoloader($dumpAutoloader = true)
|
||||||
|
@ -1194,7 +1391,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* set whether to run scripts or not
|
* set whether to run scripts or not
|
||||||
*
|
*
|
||||||
* @param boolean $runScripts
|
* @param bool $runScripts
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setRunScripts($runScripts = true)
|
public function setRunScripts($runScripts = true)
|
||||||
|
@ -1220,7 +1417,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* run in verbose mode
|
* run in verbose mode
|
||||||
*
|
*
|
||||||
* @param boolean $verbose
|
* @param bool $verbose
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setVerbose($verbose = true)
|
public function setVerbose($verbose = true)
|
||||||
|
@ -1243,7 +1440,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* set ignore Platform Package requirements
|
* set ignore Platform Package requirements
|
||||||
*
|
*
|
||||||
* @param boolean $ignorePlatformReqs
|
* @param bool $ignorePlatformReqs
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setIgnorePlatformRequirements($ignorePlatformReqs = false)
|
public function setIgnorePlatformRequirements($ignorePlatformReqs = false)
|
||||||
|
@ -1270,7 +1467,7 @@ class Installer
|
||||||
/**
|
/**
|
||||||
* Should dependencies of whitelisted packages be updated recursively?
|
* Should dependencies of whitelisted packages be updated recursively?
|
||||||
*
|
*
|
||||||
* @param boolean $updateDependencies
|
* @param bool $updateDependencies
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setWhitelistDependencies($updateDependencies = true)
|
public function setWhitelistDependencies($updateDependencies = true)
|
||||||
|
@ -1281,9 +1478,9 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should packages be prefered in a stable version when updating?
|
* Should packages be preferred in a stable version when updating?
|
||||||
*
|
*
|
||||||
* @param boolean $preferStable
|
* @param bool $preferStable
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setPreferStable($preferStable = true)
|
public function setPreferStable($preferStable = true)
|
||||||
|
@ -1294,9 +1491,9 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should packages be prefered in a lowest version when updating?
|
* Should packages be preferred in a lowest version when updating?
|
||||||
*
|
*
|
||||||
* @param boolean $preferLowest
|
* @param bool $preferLowest
|
||||||
* @return Installer
|
* @return Installer
|
||||||
*/
|
*/
|
||||||
public function setPreferLowest($preferLowest = true)
|
public function setPreferLowest($preferLowest = true)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
namespace Composer\Installer;
|
namespace Composer\Installer;
|
||||||
|
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
|
@ -89,9 +90,8 @@ class InstallationManager
|
||||||
*
|
*
|
||||||
* @param string $type package type
|
* @param string $type package type
|
||||||
*
|
*
|
||||||
* @return InstallerInterface
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException if installer for provided type is not registered
|
* @throws \InvalidArgumentException if installer for provided type is not registered
|
||||||
|
* @return InstallerInterface
|
||||||
*/
|
*/
|
||||||
public function getInstaller($type)
|
public function getInstaller($type)
|
||||||
{
|
{
|
||||||
|
@ -231,9 +231,16 @@ class InstallationManager
|
||||||
return $installer->getInstallPath($package);
|
return $installer->getInstallPath($package);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notifyInstalls()
|
public function notifyInstalls(IOInterface $io)
|
||||||
{
|
{
|
||||||
foreach ($this->notifiablePackages as $repoUrl => $packages) {
|
foreach ($this->notifiablePackages as $repoUrl => $packages) {
|
||||||
|
$repositoryName = parse_url($repoUrl, PHP_URL_HOST);
|
||||||
|
if ($io->hasAuthentication($repositoryName)) {
|
||||||
|
$auth = $io->getAuthentication($repositoryName);
|
||||||
|
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
|
||||||
|
$authHeader = 'Authorization: Basic '.$authStr;
|
||||||
|
}
|
||||||
|
|
||||||
// non-batch API, deprecated
|
// non-batch API, deprecated
|
||||||
if (strpos($repoUrl, '%package%')) {
|
if (strpos($repoUrl, '%package%')) {
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
|
@ -249,8 +256,11 @@ class InstallationManager
|
||||||
'header' => array('Content-type: application/x-www-form-urlencoded'),
|
'header' => array('Content-type: application/x-www-form-urlencoded'),
|
||||||
'content' => http_build_query($params, '', '&'),
|
'content' => http_build_query($params, '', '&'),
|
||||||
'timeout' => 3,
|
'timeout' => 3,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
if (isset($authHeader)) {
|
||||||
|
$opts['http']['header'][] = $authHeader;
|
||||||
|
}
|
||||||
|
|
||||||
$context = StreamContextFactory::getContext($url, $opts);
|
$context = StreamContextFactory::getContext($url, $opts);
|
||||||
@file_get_contents($url, false, $context);
|
@file_get_contents($url, false, $context);
|
||||||
|
@ -273,8 +283,11 @@ class InstallationManager
|
||||||
'header' => array('Content-Type: application/json'),
|
'header' => array('Content-Type: application/json'),
|
||||||
'content' => json_encode($postData),
|
'content' => json_encode($postData),
|
||||||
'timeout' => 6,
|
'timeout' => 6,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
if (isset($authHeader)) {
|
||||||
|
$opts['http']['header'][] = $authHeader;
|
||||||
|
}
|
||||||
|
|
||||||
$context = StreamContextFactory::getContext($repoUrl, $opts);
|
$context = StreamContextFactory::getContext($repoUrl, $opts);
|
||||||
@file_get_contents($repoUrl, false, $context);
|
@file_get_contents($repoUrl, false, $context);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue