Merge remote-tracking branch 'origin/master' into tls-config
commit
5d015defb8
|
@ -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
|
||||||
|
|
83
CHANGELOG.md
83
CHANGELOG.md
|
@ -1,4 +1,54 @@
|
||||||
### 1.0.0-alpha9 (2014-12-07)
|
### [1.0.0-alpha11] - 2015-11-14
|
||||||
|
|
||||||
|
* 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 possibility to reference script handlers from within other handlers using @script-name to reduce duplication
|
||||||
|
* 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 +78,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 +109,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 +160,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 +171,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 +185,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 +203,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 +231,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 +253,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 +268,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.6",
|
||||||
"seld/jsonlint": "~1.0",
|
"composer/spdx-licenses": "^1.0",
|
||||||
"symfony/console": "~2.3",
|
"composer/semver": "^1.0",
|
||||||
"symfony/finder": "~2.2",
|
"seld/jsonlint": "^1.4",
|
||||||
"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.0"
|
"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
163
doc/00-intro.md
163
doc/00-intro.md
|
@ -1,54 +1,42 @@
|
||||||
# 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 does not install anything globally. Thus, it is a dependency
|
||||||
|
manager. It does however support a "global" project for convenience via the
|
||||||
|
[global](03-cli.md#global) command.
|
||||||
|
|
||||||
This idea is not new and Composer is strongly inspired by node's [npm](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 +48,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 +73,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,17 +128,18 @@ 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
|
||||||
|
@ -135,7 +147,7 @@ C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** If the above fails due to readfile, enable php_openssl.dll in php.ini.
|
> **Note:** If the above fails due to readfile, enable php_openssl.dll in php.ini.
|
||||||
> You may use the http URL, however this will leave the request susceptible to a
|
> You may use the `http` URL, however this will leave the request susceptible to a
|
||||||
> Man-In-The-Middle (MITM) attack.
|
> Man-In-The-Middle (MITM) attack.
|
||||||
|
|
||||||
Create a new `composer.bat` file alongside `composer.phar`:
|
Create a new `composer.bat` file alongside `composer.phar`:
|
||||||
|
@ -144,6 +156,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
|
||||||
|
@ -153,46 +167,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.
|
|
||||||
|
|
||||||
> **Note:** Composer will attempt to protect all HTTPS requests using SSL/TLS. It
|
|
||||||
> implements peer verification using a certificate bundle, either one installed on
|
|
||||||
> the local system or a copy distributed with Composer. You may also pass the path
|
|
||||||
> to a bundle using the --cafile option for most commands. While you can also
|
|
||||||
> disable peer verification by passing the --disable-tls option, this is not
|
|
||||||
> recommended and will leave all downloads susceptible to Man-In-The-Middle (MITM)
|
|
||||||
> attacks.
|
|
||||||
|
|
||||||
## 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) →
|
||||||
|
|
149
doc/03-cli.md
149
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`.
|
* **--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.
|
||||||
|
@ -98,6 +99,8 @@ resolution.
|
||||||
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.
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
|
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
|
||||||
|
Implicitly enables `--optimize-autoloader`.
|
||||||
|
|
||||||
## update
|
## update
|
||||||
|
|
||||||
|
@ -129,10 +132,10 @@ 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`.
|
* **--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.
|
||||||
|
@ -141,11 +144,13 @@ 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.
|
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
|
* **--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`.
|
||||||
|
@ -175,16 +180,22 @@ 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.
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
|
* **--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
|
||||||
|
|
||||||
|
@ -201,13 +212,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
|
||||||
|
|
||||||
|
@ -215,6 +231,9 @@ The global command allows you to run other commands like `install`, `require`
|
||||||
or `update` as if you were running them from the [COMPOSER_HOME](#composer-home)
|
or `update` as if you were running them from the [COMPOSER_HOME](#composer-home)
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
|
This is merely a helper to manage a project stored in a central location that
|
||||||
|
can hold CLI tools or Composer plugins that you want to have available everywhere.
|
||||||
|
|
||||||
This can be used to install CLI utilities globally and if you add
|
This can be used to install CLI utilities globally and if you add
|
||||||
`$COMPOSER_HOME/vendor/bin` to your `$PATH` environment variable. Here is an
|
`$COMPOSER_HOME/vendor/bin` to your `$PATH` environment variable. Here is an
|
||||||
example:
|
example:
|
||||||
|
@ -267,8 +286,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 +312,7 @@ php composer.phar show monolog/monolog 1.0.2
|
||||||
* **--self (-s):** List the root package info.
|
* **--self (-s):** List the root package info.
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
|
* **--tree (-t):** List the dependencies as a tree. Only usable when giving a single package name or combined with `-i`.
|
||||||
|
|
||||||
## browse / home
|
## browse / home
|
||||||
|
|
||||||
|
@ -303,6 +323,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
|
||||||
|
@ -312,17 +344,21 @@ should be included in the listing. By default both are used.
|
||||||
```sh
|
```sh
|
||||||
php composer.phar depends --link-type=require monolog/monolog
|
php composer.phar depends --link-type=require monolog/monolog
|
||||||
|
|
||||||
nrk/monolog-fluent
|
nrk/monolog-fluent requires monolog/monolog (~1.8)
|
||||||
poc/poc
|
poc/poc requires monolog/monolog (^1.6)
|
||||||
propel/propel
|
propel/propel requires monolog/monolog (1.*)
|
||||||
symfony/monolog-bridge
|
symfony/monolog-bridge requires monolog/monolog (>=1.2)
|
||||||
symfony/symfony
|
symfony/symfony requires monolog/monolog (~1)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--link-type:** The link types to match on, can be specified multiple
|
* **--link-type:** The link types to match on, can be specified multiple
|
||||||
times.
|
times.
|
||||||
|
* **--match-constraint (-m):** Filters the dependencies shown using this constraint.
|
||||||
|
* **--invert-match-constraint (-i):** Turns --match-constraint around into a blacklist
|
||||||
|
insteead of a whitelist.
|
||||||
|
* **--with-replaces:** Search for replaced packages as well.
|
||||||
|
|
||||||
## validate
|
## validate
|
||||||
|
|
||||||
|
@ -336,7 +372,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
|
||||||
|
|
||||||
|
@ -361,7 +399,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
|
||||||
|
@ -374,7 +412,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
|
||||||
|
@ -384,13 +422,14 @@ 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.
|
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
|
* **--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
|
||||||
|
@ -405,8 +444,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
|
||||||
|
|
||||||
|
@ -430,13 +468,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:
|
||||||
|
@ -446,7 +484,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.
|
||||||
|
|
||||||
|
@ -479,14 +517,11 @@ By default the command checks for the packages on packagist.org.
|
||||||
* **--keep-vcs:** Skip the deletion of the VCS metadata for the created
|
* **--keep-vcs:** Skip the deletion of the VCS metadata for the created
|
||||||
project. This is mostly useful if you run the command in non-interactive
|
project. This is mostly useful if you run the command in non-interactive
|
||||||
mode.
|
mode.
|
||||||
<<<<<<< HEAD
|
|
||||||
* **--disable-tls:** Display SSL/TLS peer verification.
|
* **--disable-tls:** Display SSL/TLS peer verification.
|
||||||
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
* **--cafile:** If specified, use the given certificate file for SSL/TLS peer verification.
|
||||||
=======
|
|
||||||
* **--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.
|
||||||
>>>>>>> master
|
|
||||||
|
|
||||||
## dump-autoload
|
## dump-autoload
|
||||||
|
|
||||||
|
@ -506,6 +541,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
|
||||||
|
@ -524,8 +561,13 @@ Lists the name, version and license of every package installed. Use
|
||||||
|
|
||||||
## run-script
|
## run-script
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--no-dev:** Disable dev mode
|
||||||
|
* **--list:** List user defined scripts
|
||||||
|
|
||||||
To run [scripts](articles/scripts.md) manually you can use this command,
|
To run [scripts](articles/scripts.md) manually you can use this command,
|
||||||
just give it the script name and optionally --no-dev to disable the dev mode.
|
just give it the script name and optionally any required arguments.
|
||||||
|
|
||||||
## diagnose
|
## diagnose
|
||||||
|
|
||||||
|
@ -566,6 +608,11 @@ To get more information about a certain command, just use `help`.
|
||||||
php composer.phar help install
|
php composer.phar help install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Command-line completion
|
||||||
|
|
||||||
|
Command-line completion can be enabled by following instructions
|
||||||
|
[on this page](https://github.com/bamarni/symfony-console-autocomplete).
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
You can set a number of environment variables that override certain settings.
|
You can set a number of environment variables that override certain settings.
|
||||||
|
@ -584,6 +631,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
|
||||||
|
@ -591,7 +640,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
|
||||||
|
@ -601,7 +650,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.
|
||||||
|
|
||||||
|
@ -623,18 +672,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.
|
||||||
|
|
||||||
|
@ -648,23 +697,23 @@ 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_CAFILE
|
### COMPOSER_CAFILE
|
||||||
|
@ -674,11 +723,15 @@ file to be used during SSL/TLS peer verification.
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
|
### COMPOSER_DISABLE_XDEBUG_WARN
|
||||||
|
|
||||||
|
If set to 1, this env disables the warning about having xdebug enabled.
|
||||||
|
|
||||||
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
||||||
|
|
200
doc/04-schema.md
200
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
|
||||||
|
@ -329,12 +327,26 @@ It is also possible to inline-alias a package constraint so that it matches
|
||||||
a constraint that it otherwise would not. For more information [see the
|
a constraint that it otherwise would not. For more information [see the
|
||||||
aliases article](articles/aliases.md).
|
aliases article](articles/aliases.md).
|
||||||
|
|
||||||
|
`require` and `require-dev` also support references to specific PHP versions
|
||||||
|
and PHP extensions your project needs to run successfully.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require" : {
|
||||||
|
"php" : "^5.5 || ^7.0",
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### require
|
#### require
|
||||||
|
|
||||||
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 +566,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 +659,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 +674,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 +683,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 +696,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 +735,7 @@ Example:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "pear",
|
"type": "pear",
|
||||||
"url": "http://pear2.php.net"
|
"url": "https://pear2.php.net"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "package",
|
"type": "package",
|
||||||
|
@ -715,7 +747,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,99 +762,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.
|
|
||||||
* **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.
|
||||||
|
@ -878,4 +823,39 @@ The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`,
|
||||||
|
|
||||||
Optional.
|
Optional.
|
||||||
|
|
||||||
|
### 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 strings.
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
version of the parent branch or at least master or something.
|
||||||
|
|
||||||
|
To handle non-numeric named branches as versions instead of searching for a parent branch
|
||||||
|
with a valid version or special branch name like master, you can set patterns for branch
|
||||||
|
names, that should be handled as dev version branches.
|
||||||
|
|
||||||
|
This is really helpful when you have dependencies using "self.version", so that not dev-master,
|
||||||
|
but the same branch is installed (in the example: latest-testing).
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
If you have a testing branch, that is heavily maintained during a testing phase and is
|
||||||
|
deployed to your staging environment, normally "composer show -s" will give you `versions : * dev-master`.
|
||||||
|
|
||||||
|
If you configure `latest-.*` as a pattern for non-feature-branches like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"non-feature-branches": ["latest-.*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then "composer show -s" will give you `versions : * dev-latest-testing`.
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) →
|
← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) →
|
||||||
|
|
|
@ -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
|
||||||
|
@ -234,7 +234,7 @@ project to use the patched version. If the library is on GitHub (this is the
|
||||||
case most of the time), you can simply fork it there and push your changes to
|
case most of the time), you can simply fork it there and push your changes to
|
||||||
your fork. After that you update the project's `composer.json`. All you have
|
your fork. After that you update the project's `composer.json`. All you have
|
||||||
to do is add your fork as a repository and update the version constraint to
|
to do is add your fork as a repository and update the version constraint to
|
||||||
point to your custom branch. For version constraint naming conventions see
|
point to your custom branch. Your custom branch name must be prefixed with `"dev-"`. For version constraint naming conventions see
|
||||||
[Libraries](02-libraries.md) for more information.
|
[Libraries](02-libraries.md) for more information.
|
||||||
|
|
||||||
Example assuming you patched monolog to fix a bug in the `bugfix` branch:
|
Example assuming you patched monolog to fix a bug in the `bugfix` branch:
|
||||||
|
@ -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,
|
||||||
|
@ -533,19 +537,22 @@ There are a few tools that can help you create a `composer` repository.
|
||||||
### Packagist
|
### Packagist
|
||||||
|
|
||||||
The underlying application used by packagist is open source. This means that you
|
The underlying application used by packagist is open source. This means that you
|
||||||
can just install your own copy of packagist, re-brand, and use it. It's really
|
can technically install your own copy of packagist. However it is not a
|
||||||
quite straight-forward to do. However due to its size and complexity, for most
|
supported use case and changes will happen without caring for third parties
|
||||||
small and medium sized companies willing to track a few packages will be better
|
using the code.
|
||||||
off using Satis.
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
To set your own copy, simply follow the instructions from the [packagist
|
### Toran Proxy
|
||||||
github repository](https://github.com/composer/packagist).
|
|
||||||
|
[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)
|
||||||
|
for more information.
|
||||||
|
|
||||||
### Satis
|
### Satis
|
||||||
|
|
||||||
|
@ -599,6 +606,49 @@ 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": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The local package will be symlinked if possible, in which case the output in
|
||||||
|
the console will read `Symlinked from ../../packages/my-package`. If symlinking
|
||||||
|
is _not_ possible the package will be copied. In that case, the console will
|
||||||
|
output `Mirrored from ../../packages/my-package`.
|
||||||
|
|
||||||
|
Instead of using a relative path, an absolute path can also be used.
|
||||||
|
|
||||||
|
> **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
|
||||||
|
@ -614,4 +664,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,189 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
## sort-packages
|
||||||
|
|
||||||
|
Defaults to `false`. If `true`, the `require` command keeps packages sorted
|
||||||
|
by name in `composer.json` when adding a new package.
|
||||||
|
|
||||||
|
## 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,17 @@ 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 [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/).
|
||||||
> 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:
|
||||||
|
@ -159,7 +159,7 @@ class TemplateInstaller extends LibraryInstaller
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getPackageBasePath(PackageInterface $package)
|
public function getInstallPath(PackageInterface $package)
|
||||||
{
|
{
|
||||||
$prefix = substr($package->getPrettyName(), 0, 23);
|
$prefix = substr($package->getPrettyName(), 0, 23);
|
||||||
if ('phpdocumentor/template-' !== $prefix) {
|
if ('phpdocumentor/template-' !== $prefix) {
|
||||||
|
|
|
@ -6,16 +6,22 @@
|
||||||
|
|
||||||
# Toran Proxy
|
# Toran Proxy
|
||||||
|
|
||||||
[Toran Proxy](https://toranproxy.com/) is a commercial alternative to Satis offering professional support as well as a web UI to manage everything and a better integration with Composer.
|
[Toran Proxy](https://toranproxy.com/) is a commercial alternative to Satis
|
||||||
|
offering professional support as well as a web UI to manage everything and a
|
||||||
|
better integration with Composer. It also provides proxying/mirroring for git
|
||||||
|
repos and package zip files which makes installs faster and independent from
|
||||||
|
third party systems.
|
||||||
|
|
||||||
Toran's revenue is also used to pay for Composer and Packagist development and hosting so using it is a good way to support open source financially. You can find more information about how to set it up and use it on the [Toran Proxy](https://toranproxy.com/) website.
|
Toran's revenue is also used to pay for Composer and Packagist development and
|
||||||
|
hosting so using it is a good way to support open source financially. You can
|
||||||
|
find more information about how to set it up and use it on the [Toran Proxy](https://toranproxy.com/) website.
|
||||||
|
|
||||||
# Satis
|
# Satis
|
||||||
|
|
||||||
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`.
|
||||||
|
|
||||||
|
@ -38,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
|
||||||
}
|
}
|
||||||
|
@ -54,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": "*",
|
||||||
|
@ -66,8 +72,8 @@ constraint if you want really specific versions.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you did 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
|
||||||
|
@ -84,6 +90,29 @@ Set up a virtual-host that points to that `web/` directory, let's say it is
|
||||||
`packages.example.org`. Alternatively, with PHP >= 5.4.0, you can use the built-in
|
`packages.example.org`. Alternatively, with PHP >= 5.4.0, you can use the built-in
|
||||||
CLI server `php -S localhost:port -t satis-output-dir/` for a temporary solution.
|
CLI server `php -S localhost:port -t satis-output-dir/` for a temporary solution.
|
||||||
|
|
||||||
|
### Partial Updates
|
||||||
|
|
||||||
|
You can tell Satis to selectively update only particular packages or process only
|
||||||
|
a repository with a given URL. This cuts down the time it takes to rebuild the
|
||||||
|
`package.json` file and is helpful if you use (custom) webhooks to trigger rebuilds
|
||||||
|
whenever code is pushed into one of your repositories.
|
||||||
|
|
||||||
|
To rebuild only particular packages, pass the package names on the command line like
|
||||||
|
so:
|
||||||
|
```
|
||||||
|
php bin/satis build satis.json web/ this/package that/other-package
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that
|
||||||
|
this will still need to pull and scan all of your VCS repositories because any VCS
|
||||||
|
repository might contain (on any branch) one of the selected packages.
|
||||||
|
|
||||||
|
If you want to scan only a single repository and update all packages found in it,
|
||||||
|
pass the VCS repository URL as an optional argument:
|
||||||
|
```
|
||||||
|
php bin/satis build --repository-url https://only.my/repo.git satis.json web/
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
In your projects all you need to add now is your own composer repository using
|
In your projects all you need to add now is your own composer repository using
|
||||||
|
@ -129,7 +158,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:
|
||||||
|
|
||||||
|
@ -149,7 +178,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
|
||||||
|
|
||||||
|
@ -176,10 +243,14 @@ following to your `satis.json`:
|
||||||
|
|
||||||
#### Options explained
|
#### Options explained
|
||||||
|
|
||||||
* `directory`: the location of the dist files (inside the `output-dir`)
|
* `directory`: required, the location of the dist files (inside the `output-dir`)
|
||||||
* `format`: optional, `zip` (default) or `tar`
|
* `format`: optional, `zip` (default) or `tar`
|
||||||
* `prefix-url`: optional, location of the downloads, homepage (from `satis.json`) followed by `directory` by default
|
* `prefix-url`: optional, location of the downloads, homepage (from `satis.json`) followed by `directory` by default
|
||||||
* `skip-dev`: optional, `false` by default, when enabled (`true`) satis will not create downloads for branches
|
* `skip-dev`: optional, `false` by default, when enabled (`true`) satis will not create downloads for branches
|
||||||
|
* `absolute-directory`: optional, a _local_ directory where the dist files are dumped instead of `output-dir`/`directory`
|
||||||
|
* `whitelist`: optional, if set as a list of package names, satis will only dump the dist files of these packages
|
||||||
|
* `blacklist`: optional, if set as a list of package names, satis will not dump the dist files of these packages
|
||||||
|
* `checksum`: optional, `true` by default, when disabled (`false`) satis will not provide the sha1 checksum for the dist files
|
||||||
|
|
||||||
Once enabled, all downloads (include those from GitHub and BitBucket) will be replaced with a _local_ version.
|
Once enabled, all downloads (include those from GitHub and BitBucket) will be replaced with a _local_ version.
|
||||||
|
|
||||||
|
@ -188,9 +259,31 @@ 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`.
|
||||||
|
|
||||||
|
### Web outputs
|
||||||
|
|
||||||
|
* `output-html`: optional, `true` by default, when disabled (`false`) satis will not generate the `output-dir`/index.html page.
|
||||||
|
* `twig-template`: optional, a path to a personalized [Twig](http://twig.sensiolabs.org/) template for the `output-dir`/index.html page.
|
||||||
|
|
||||||
|
### Abandoned packages
|
||||||
|
|
||||||
|
To enable your satis installation to indicate that some packages are abandoned, add the following to your `satis.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"abandoned": {
|
||||||
|
"company/package": true,
|
||||||
|
"company/package2": "company/newpackage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `true` value indicates that the package is truly abandoned while the `"company/newpackage"` value specifies that the package is replaced by
|
||||||
|
the `company/newpackage` package.
|
||||||
|
|
||||||
|
Note that all packages set as abandoned in their own `composer.json` file will be marked abandoned as well.
|
||||||
|
|
||||||
### Resolving dependencies
|
### Resolving dependencies
|
||||||
|
|
||||||
|
@ -209,3 +302,11 @@ When searching for packages, satis will attempt to resolve all the required pack
|
||||||
Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`.
|
Therefore, if you are requiring a package from Packagist, you will need to define it in your `satis.json`.
|
||||||
|
|
||||||
Dev dependencies are packaged only if the `require-dev-dependencies` parameter is set to true.
|
Dev dependencies are packaged only if the `require-dev-dependencies` parameter is set to true.
|
||||||
|
|
||||||
|
### Other options
|
||||||
|
|
||||||
|
* `output-dir`: optional, defines where to output the repository files
|
||||||
|
if not provided as an argument when calling the `build` command.
|
||||||
|
* `config`: optional, lets you define all config options from composer, except `archive-format` and `archive-dir` as the configuration is done through [archive](#downloads) instead. See
|
||||||
|
(http://getcomposer.org/doc/04-schema.md#config)
|
||||||
|
* `notify-batch`: optional, specify a URL that will be called every time a user installs a package. See (https://getcomposer.org/doc/05-repositories.md#notify-batch)
|
||||||
|
|
|
@ -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,6 +100,9 @@ Script definition example:
|
||||||
"MyVendor\\MyClass::warmCache",
|
"MyVendor\\MyClass::warmCache",
|
||||||
"phpunit -c app/"
|
"phpunit -c app/"
|
||||||
],
|
],
|
||||||
|
"post-autoload-dump": [
|
||||||
|
"MyVendor\\MyClass::postAutoloadDump"
|
||||||
|
],
|
||||||
"post-create-project-cmd": [
|
"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
|
||||||
|
|
||||||
|
@ -161,3 +200,20 @@ simply running `composer test`:
|
||||||
|
|
||||||
> **Note:** Composer's bin-dir is pushed on top of the PATH so that binaries
|
> **Note:** Composer's bin-dir is pushed on top of the PATH so that binaries
|
||||||
> of dependencies are easily accessible as CLI commands when writing scripts.
|
> of dependencies are easily accessible as CLI commands when writing scripts.
|
||||||
|
|
||||||
|
## Referencing scripts
|
||||||
|
|
||||||
|
To enable script re-use and avoid duplicates, you can call a script from another
|
||||||
|
one by prefixing the command name with `@`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"@clearCache",
|
||||||
|
"phpunit"
|
||||||
|
],
|
||||||
|
"clearCache": "rm -rf cache/*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
@ -85,11 +100,14 @@ If composer shows memory errors on some commands:
|
||||||
|
|
||||||
`PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...>`
|
`PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...>`
|
||||||
|
|
||||||
The PHP `memory_limit` should be increased.
|
Check first that XDebug is not loaded in your `php.ini` by running
|
||||||
|
`composer diagnose`. If XDebug is loaded, you should disable it by
|
||||||
|
commenting the line `zend_extension=path/to/xdebug` in your `php.ini`.
|
||||||
|
Don't forget to enable XDebug again after using Composer, if you need it.
|
||||||
|
|
||||||
> **Note:** Composer internally increases the `memory_limit` to `512M`.
|
If composer still raises the error, the PHP `memory_limit` should be increased.
|
||||||
> 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.
|
> **Note:** Composer internally increases the `memory_limit` to `1G`.
|
||||||
|
|
||||||
To get the current `memory_limit` value, run:
|
To get the current `memory_limit` value, run:
|
||||||
|
|
||||||
|
@ -101,7 +119,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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -111,6 +129,47 @@ Or, you can increase the limit with a command-line argument:
|
||||||
php -d memory_limit=-1 composer.phar <...>
|
php -d memory_limit=-1 composer.phar <...>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Xdebug impact on Composer
|
||||||
|
|
||||||
|
Running Composer console commands while the php extension "xdebug" is loaded reduces speed considerably.
|
||||||
|
This is even the case when all "xdebug" related features are disabled per php.ini flags,
|
||||||
|
but the php extension itself is loaded into the PHP engine.
|
||||||
|
Compared to a cli command run with "xdebug" enabled a speed improvement by a factor of up to 3 is not uncommon.
|
||||||
|
|
||||||
|
> **Note:** This is a general issue when running PHP with "xdebug" enabled. You shouldn't
|
||||||
|
> load the extension in production like environments per se.
|
||||||
|
|
||||||
|
Disable "xdebug" in your `php.ini` (ex. `/etc/php5/cli/php.ini` for Debian-like systems) by
|
||||||
|
locating the related `zend_extension` directive and prepending it with `;` (semicolon):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
;zend_extension = "/path/to/my/xdebug.so"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you disable this extension and still want it to be added on `php` cli command, you can deal with aliases on *nix systems:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Load xdebug Zend extension with php command
|
||||||
|
alias php='php -dzend_extension=xdebug.so'
|
||||||
|
# PHPUnit needs xdebug for coverage. In this case, just make an alias with php command prefix.
|
||||||
|
alias phpunit='php $(which phpunit)'
|
||||||
|
```
|
||||||
|
|
||||||
|
With that, all php binaries called directly **will not** have xdebug enabled
|
||||||
|
but you will still have it by prefixing them with php command.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Will NOT have xdebug enabled
|
||||||
|
composer update
|
||||||
|
# Will have xdebug enabled by alias
|
||||||
|
php /usr/local/bin/composer update
|
||||||
|
```
|
||||||
|
|
||||||
|
If you do not want to disable it and want to get rid of the warning you can also define the
|
||||||
|
[COMPOSER_DISABLE_XDEBUG_WARN](../03-cli.md#composer-disable-xdebug-warn) environment variable.
|
||||||
|
|
||||||
## "The system cannot find the path specified" (Windows)
|
## "The system cannot find the path specified" (Windows)
|
||||||
|
|
||||||
1. Open regedit.
|
1. Open regedit.
|
||||||
|
@ -157,3 +216,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.
|
||||||
|
|
|
@ -14,7 +14,7 @@ compatible with the new major version of your dependency.
|
||||||
|
|
||||||
For example instead of using `>=3.4` you should use `~3.4` which allows all
|
For example instead of using `>=3.4` you should use `~3.4` which allows all
|
||||||
versions up to `3.999` but does not include `4.0` and above. The `~` operator
|
versions up to `3.999` but does not include `4.0` and above. The `~` operator
|
||||||
works very well with libraries follow [semantic versioning](http://semver.org).
|
works very well with libraries following [semantic versioning](http://semver.org).
|
||||||
|
|
||||||
**Note:** As a package maintainer, you can make the life of your users easier
|
**Note:** As a package maintainer, you can make the life of your users easier
|
||||||
by providing an [alias version](../articles/aliases.md) for your development
|
by providing an [alias version](../articles/aliases.md) for your development
|
||||||
|
|
|
@ -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",
|
||||||
|
@ -136,6 +136,11 @@
|
||||||
"description": "A hash of domain name => github API oauth tokens, typically {\"github.com\":\"<token>\"}.",
|
"description": "A hash of domain name => github API oauth tokens, typically {\"github.com\":\"<token>\"}.",
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
},
|
},
|
||||||
|
"gitlab-oauth": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A hash of domain name => gitlab API oauth tokens, typically {\"gitlab.com\":\"<token>\"}.",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
"http-basic": {
|
"http-basic": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "A hash of domain name => {\"username\": \"...\", \"password\": \"...\"}.",
|
"description": "A hash of domain name => {\"username\": \"...\", \"password\": \"...\"}.",
|
||||||
|
@ -145,6 +150,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 +191,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 +225,21 @@
|
||||||
"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."
|
||||||
|
},
|
||||||
|
"gitlab-domains": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of domains to use in gitlab mode. This is used for custom GitLab setups, defaults to [\"gitlab.com\"].",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 +269,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/\"]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -284,11 +317,12 @@
|
||||||
},
|
},
|
||||||
"minimum-stability": {
|
"minimum-stability": {
|
||||||
"type": ["string"],
|
"type": ["string"],
|
||||||
"description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable."
|
"description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable.",
|
||||||
|
"pattern": "^dev|alpha|beta|rc|RC|stable$"
|
||||||
},
|
},
|
||||||
"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"],
|
||||||
|
@ -384,17 +418,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": {
|
||||||
|
@ -406,8 +440,24 @@
|
||||||
"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": {
|
||||||
|
"type": ["array"],
|
||||||
|
"description": "A set of string or regex patterns for non-numeric branch names that will not be handled as feature branches.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abandoned": {
|
||||||
|
"type": ["boolean", "string"],
|
||||||
|
"description": "Indicates whether this package has been abandoned, it can be boolean or a package name/URL pointing to a recommended alternative. Defaults to false."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,21 @@ class AutoloadGenerator
|
||||||
*/
|
*/
|
||||||
private $io;
|
private $io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
private $devMode = false;
|
private $devMode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $runScripts = false;
|
||||||
|
|
||||||
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
|
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
|
||||||
{
|
{
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
|
@ -51,11 +64,38 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to run scripts or not
|
||||||
|
*
|
||||||
|
* @param bool $runScripts
|
||||||
|
*/
|
||||||
|
public function setRunScripts($runScripts = true)
|
||||||
|
{
|
||||||
|
$this->runScripts = (boolean) $runScripts;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
if ($this->runScripts) {
|
||||||
$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,
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$filesystem = new Filesystem();
|
$filesystem = new Filesystem();
|
||||||
$filesystem->ensureDirectoryExists($config->get('vendor-dir'));
|
$filesystem->ensureDirectoryExists($config->get('vendor-dir'));
|
||||||
|
@ -63,7 +103,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 +210,43 @@ 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(
|
|
||||||
'{%s/%s.+(?<!(?<!/)Test\.php)$}',
|
|
||||||
preg_quote($dir),
|
|
||||||
($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);
|
||||||
|
@ -213,35 +255,68 @@ EOF;
|
||||||
}
|
}
|
||||||
$classmapFile .= ");\n";
|
$classmapFile .= ");\n";
|
||||||
|
|
||||||
|
if (!$suffix) {
|
||||||
|
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) {
|
if (!$suffix) {
|
||||||
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
|
$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);
|
|
||||||
|
|
||||||
|
if ($this->runScripts) {
|
||||||
$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)
|
||||||
{
|
{
|
||||||
|
@ -301,13 +376,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 +414,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,12 +470,13 @@ EOF;
|
||||||
protected function getIncludeFilesFile(array $files, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode)
|
protected function getIncludeFilesFile(array $files, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode)
|
||||||
{
|
{
|
||||||
$filesCode = '';
|
$filesCode = '';
|
||||||
foreach ($files as $functionFile) {
|
foreach ($files as $fileIdentifier => $functionFile) {
|
||||||
$filesCode .= ' '.$this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile).",\n";
|
$filesCode .= ' ' . var_export($fileIdentifier, true) . ' => '
|
||||||
|
. $this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile) . ",\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$filesCode) {
|
if (!$filesCode) {
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <<<EOF
|
return <<<EOF
|
||||||
|
@ -444,15 +537,8 @@ 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
|
|
||||||
// when APC has been fixed:
|
|
||||||
// - https://github.com/composer/composer/issues/959
|
|
||||||
// - https://bugs.php.net/bug.php?id=52144
|
|
||||||
// - https://bugs.php.net/bug.php?id=61576
|
|
||||||
// - https://bugs.php.net/bug.php?id=59298
|
|
||||||
|
|
||||||
$file = <<<HEADER
|
$file = <<<HEADER
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
@ -492,23 +578,21 @@ HEADER;
|
||||||
INCLUDE_PATH;
|
INCLUDE_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
$file .= <<<'PSR0'
|
if (!$this->classMapAuthoritative) {
|
||||||
|
$file .= <<<'PSR04'
|
||||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
foreach ($map as $namespace => $path) {
|
foreach ($map as $namespace => $path) {
|
||||||
$loader->set($namespace, $path);
|
$loader->set($namespace, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PSR0;
|
|
||||||
|
|
||||||
$file .= <<<'PSR4'
|
|
||||||
$map = require __DIR__ . '/autoload_psr4.php';
|
$map = require __DIR__ . '/autoload_psr4.php';
|
||||||
foreach ($map as $namespace => $path) {
|
foreach ($map as $namespace => $path) {
|
||||||
$loader->setPsr4($namespace, $path);
|
$loader->setPsr4($namespace, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PSR4;
|
PSR04;
|
||||||
|
}
|
||||||
|
|
||||||
if ($useClassMap) {
|
if ($useClassMap) {
|
||||||
$file .= <<<'CLASSMAP'
|
$file .= <<<'CLASSMAP'
|
||||||
|
@ -521,7 +605,7 @@ PSR4;
|
||||||
CLASSMAP;
|
CLASSMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($classMapAuthoritative) {
|
if ($this->classMapAuthoritative) {
|
||||||
$file .= <<<'CLASSMAPAUTHORITATIVE'
|
$file .= <<<'CLASSMAPAUTHORITATIVE'
|
||||||
$loader->setClassMapAuthoritative(true);
|
$loader->setClassMapAuthoritative(true);
|
||||||
|
|
||||||
|
@ -536,11 +620,11 @@ INCLUDEPATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($targetDirLoader) {
|
if ($targetDirLoader) {
|
||||||
$file .= <<<REGISTER_AUTOLOAD
|
$file .= <<<REGISTER_TARGET_DIR_AUTOLOAD
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'autoload'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'autoload'), true, true);
|
||||||
|
|
||||||
|
|
||||||
REGISTER_AUTOLOAD;
|
REGISTER_TARGET_DIR_AUTOLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
$file .= <<<REGISTER_LOADER
|
$file .= <<<REGISTER_LOADER
|
||||||
|
@ -552,8 +636,8 @@ REGISTER_LOADER;
|
||||||
if ($useIncludeFiles) {
|
if ($useIncludeFiles) {
|
||||||
$file .= <<<INCLUDE_FILES
|
$file .= <<<INCLUDE_FILES
|
||||||
\$includeFiles = require __DIR__ . '/autoload_files.php';
|
\$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||||
foreach (\$includeFiles as \$file) {
|
foreach (\$includeFiles as \$fileIdentifier => \$file) {
|
||||||
composerRequire$suffix(\$file);
|
composerRequire$suffix(\$fileIdentifier, \$file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -568,12 +652,23 @@ METHOD_FOOTER;
|
||||||
|
|
||||||
$file .= $targetDirLoader;
|
$file .= $targetDirLoader;
|
||||||
|
|
||||||
|
if ($useIncludeFiles) {
|
||||||
return $file . <<<FOOTER
|
return $file . <<<FOOTER
|
||||||
}
|
}
|
||||||
|
|
||||||
function composerRequire$suffix(\$file)
|
function composerRequire$suffix(\$fileIdentifier, \$file)
|
||||||
{
|
{
|
||||||
|
if (empty(\$GLOBALS['__composer_autoload_files'][\$fileIdentifier])) {
|
||||||
require \$file;
|
require \$file;
|
||||||
|
|
||||||
|
\$GLOBALS['__composer_autoload_files'][\$fileIdentifier] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FOOTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file . <<<FOOTER
|
||||||
}
|
}
|
||||||
|
|
||||||
FOOTER;
|
FOOTER;
|
||||||
|
@ -601,7 +696,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,9 +707,43 @@ 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);
|
||||||
|
|
||||||
|
// add support for up-level relative paths
|
||||||
|
$updir = null;
|
||||||
|
$path = preg_replace_callback(
|
||||||
|
'{^((?:(?:\\\\\\.){1,2}+/)+)}',
|
||||||
|
function ($matches) use (&$updir) {
|
||||||
|
if (isset($matches[1])) {
|
||||||
|
// undo preg_quote for the matched string
|
||||||
|
$updir = str_replace('\\.', '.', $matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
$path
|
||||||
|
);
|
||||||
|
if (empty($installPath)) {
|
||||||
|
$installPath = strtr(getcwd(), '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolvedPath = realpath($installPath . '/' . $updir);
|
||||||
|
$autoloads[] = preg_quote(strtr($resolvedPath, '\\', '/')) . '/' . $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') {
|
||||||
|
$autoloads[$this->getFileIdentifier($package, $path)] = $relativePath;
|
||||||
|
continue;
|
||||||
|
} elseif ($type === 'classmap') {
|
||||||
$autoloads[] = $relativePath;
|
$autoloads[] = $relativePath;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -627,6 +756,11 @@ FOOTER;
|
||||||
return $autoloads;
|
return $autoloads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFileIdentifier(PackageInterface $package, $path)
|
||||||
|
{
|
||||||
|
return md5($package->getName() . ':' . $path);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts packages by dependency weight
|
* Sorts packages by dependency weight
|
||||||
*
|
*
|
||||||
|
@ -718,4 +852,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
namespace Composer\Autoload;
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClassLoader implements a PSR-0 class loader
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
*
|
|
||||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
|
||||||
*
|
*
|
||||||
* $loader = new \Composer\Autoload\ClassLoader();
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
*
|
*
|
||||||
|
@ -39,6 +37,8 @@ namespace Composer\Autoload;
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see http://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see http://www.php-fig.org/psr/psr-4/
|
||||||
*/
|
*/
|
||||||
class ClassLoader
|
class ClassLoader
|
||||||
{
|
{
|
||||||
|
@ -147,7 +147,7 @@ class ClassLoader
|
||||||
* appending or prepending to the ones previously set for this namespace.
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
*
|
*
|
||||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
* @param array|string $paths The PSR-0 base directories
|
* @param array|string $paths The PSR-4 base directories
|
||||||
* @param bool $prepend Whether to prepend the directories
|
* @param bool $prepend Whether to prepend the directories
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
|
|
@ -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,8 +95,8 @@ 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->write(
|
$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,12 +43,14 @@ 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))
|
||||||
|
|| !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;
|
$this->enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function isEnabled()
|
public function isEnabled()
|
||||||
{
|
{
|
||||||
|
@ -65,7 +67,7 @@ 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)) {
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('Reading '.$this->root . $file.' from cache');
|
$this->io->writeError('Reading '.$this->root . $file.' from cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
return file_get_contents($this->root . $file);
|
return file_get_contents($this->root . $file);
|
||||||
|
@ -80,12 +82,15 @@ class Cache
|
||||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||||
|
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('Writing '.$this->root . $file.' into cache');
|
$this->io->writeError('Writing '.$this->root . $file.' into 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);
|
||||||
|
@ -98,7 +103,7 @@ class Cache
|
||||||
@disk_free_space($this->root . dirname($file))
|
@disk_free_space($this->root . dirname($file))
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->io->write($message);
|
$this->io->writeError($message);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -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->write('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,10 +143,16 @@ 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)) {
|
||||||
|
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);
|
touch($this->root . $file);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('Reading '.$this->root . $file.' from cache');
|
$this->io->writeError('Reading '.$this->root . $file.' from cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
return copy($this->root . $file, $target);
|
return copy($this->root . $file, $target);
|
||||||
|
|
|
@ -34,10 +34,10 @@ EOT
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$output->writeln(<<<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', null, InputOption::VALUE_REQUIRED, 'Write the archive to this directory'),
|
||||||
|
new InputOption('file', null, 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,42 +107,42 @@ EOT
|
||||||
$package = $this->getComposer()->getPackage();
|
$package = $this->getComposer()->getPackage();
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->write('<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;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function selectPackage(IOInterface $io, $packageName, $version = null)
|
protected function selectPackage(IOInterface $io, $packageName, $version = null)
|
||||||
{
|
{
|
||||||
$io->write('<info>Searching for the specified package.</info>');
|
$io->writeError('<info>Searching for the specified package.</info>');
|
||||||
|
|
||||||
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->write('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);
|
||||||
$io->write('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
|
$io->writeError('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
|
||||||
$io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
|
$io->writeError('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
|
||||||
$io->write('<comment>Please use a more specific constraint to pick a different package.</comment>');
|
$io->writeError('<comment>Please use a more specific constraint to pick a different package.</comment>');
|
||||||
} elseif ($packages) {
|
} elseif ($packages) {
|
||||||
$package = reset($packages);
|
$package = reset($packages);
|
||||||
$io->write('<info>Found an exact match '.$package->getPrettyString().'.</info>');
|
$io->writeError('<info>Found an exact match '.$package->getPrettyString().'.</info>');
|
||||||
} else {
|
} else {
|
||||||
$io->write('<error>Could not find a package matching '.$packageName.'.</error>');
|
$io->writeError('<error>Could not find a package matching '.$packageName.'.</error>');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,21 +51,21 @@ EOT
|
||||||
foreach ($cachePaths as $key => $cachePath) {
|
foreach ($cachePaths as $key => $cachePath) {
|
||||||
$cachePath = realpath($cachePath);
|
$cachePath = realpath($cachePath);
|
||||||
if (!$cachePath) {
|
if (!$cachePath) {
|
||||||
$io->write("<info>Cache directory does not exist ($key): $cachePath</info>");
|
$io->writeError("<info>Cache directory does not exist ($key): $cachePath</info>");
|
||||||
|
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
$cache = new Cache($io, $cachePath);
|
$cache = new Cache($io, $cachePath);
|
||||||
if (!$cache->isEnabled()) {
|
if (!$cache->isEnabled()) {
|
||||||
$io->write("<info>Cache is not enabled ($key): $cachePath</info>");
|
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
|
||||||
|
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->write("<info>Clearing cache ($key): $cachePath</info>");
|
$io->writeError("<info>Clearing cache ($key): $cachePath</info>");
|
||||||
$cache->gc(0, 0);
|
$cache->gc(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->write('<info>All caches cleared.</info>');
|
$io->writeError('<info>All caches cleared.</info>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,15 +33,25 @@ class ConfigCommand extends Command
|
||||||
protected $config;
|
protected $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Composer\Json\JsonFile
|
* @var JsonFile
|
||||||
*/
|
*/
|
||||||
protected $configFile;
|
protected $configFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Composer\Config\JsonConfigSource
|
* @var JsonConfigSource
|
||||||
*/
|
*/
|
||||||
protected $configSource;
|
protected $configSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var JsonFile
|
||||||
|
*/
|
||||||
|
protected $authConfigFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var JsonConfigSource
|
||||||
|
*/
|
||||||
|
protected $authConfigSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -56,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'),
|
||||||
|
@ -65,6 +75,15 @@ class ConfigCommand extends Command
|
||||||
This command allows you to edit some basic composer settings in either the
|
This command allows you to edit some basic composer settings in either the
|
||||||
local composer.json file or the global config.json file.
|
local composer.json file or the global config.json file.
|
||||||
|
|
||||||
|
To set a config setting:
|
||||||
|
|
||||||
|
<comment>%command.full_name% bin-dir bin/</comment>
|
||||||
|
|
||||||
|
To read a config setting:
|
||||||
|
|
||||||
|
<comment>%command.full_name% bin-dir</comment>
|
||||||
|
Outputs: <info>bin</info>
|
||||||
|
|
||||||
To edit the global config.json file:
|
To edit the global config.json file:
|
||||||
|
|
||||||
<comment>%command.full_name% --global</comment>
|
<comment>%command.full_name% --global</comment>
|
||||||
|
@ -73,7 +92,15 @@ To add a repository:
|
||||||
|
|
||||||
<comment>%command.full_name% repositories.foo vcs http://bar.com</comment>
|
<comment>%command.full_name% repositories.foo vcs http://bar.com</comment>
|
||||||
|
|
||||||
You can add a repository to the global config.json file by passing in the
|
To remove a repository (repo is a short alias for repositories):
|
||||||
|
|
||||||
|
<comment>%command.full_name% --unset repo.foo</comment>
|
||||||
|
|
||||||
|
To disable packagist:
|
||||||
|
|
||||||
|
<comment>%command.full_name% repo.packagist false</comment>
|
||||||
|
|
||||||
|
You can alter repositories in the global config.json file by passing in the
|
||||||
<info>--global</info> option.
|
<info>--global</info> option.
|
||||||
|
|
||||||
To edit the file in an external editor:
|
To edit the file in an external editor:
|
||||||
|
@ -102,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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,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);
|
||||||
|
@ -132,7 +164,7 @@ EOT
|
||||||
}
|
}
|
||||||
if ($input->getOption('global') && !$this->authConfigFile->exists()) {
|
if ($input->getOption('global') && !$this->authConfigFile->exists()) {
|
||||||
touch($this->authConfigFile->getPath());
|
touch($this->authConfigFile->getPath());
|
||||||
$this->authConfigFile->write(array('http-basic' => new \ArrayObject, 'github-oauth' => new \ArrayObject));
|
$this->authConfigFile->write(array('http-basic' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject));
|
||||||
@chmod($this->authConfigFile->getPath(), 0600);
|
@chmod($this->authConfigFile->getPath(), 0600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,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;
|
||||||
|
@ -230,55 +264,13 @@ EOT
|
||||||
$value = json_encode($value);
|
$value = json_encode($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln($value);
|
$this->getIO()->write($value);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$values = $input->getArgument('setting-value'); // what the user is trying to add/change
|
$values = $input->getArgument('setting-value'); // what the user is trying to add/change
|
||||||
|
|
||||||
// handle repositories
|
|
||||||
if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
|
|
||||||
if ($input->getOption('unset')) {
|
|
||||||
return $this->configSource->removeRepository($matches[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (2 !== count($values)) {
|
|
||||||
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->configSource->addRepository($matches[1], array(
|
|
||||||
'type' => $values[0],
|
|
||||||
'url' => $values[1],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle github-oauth
|
|
||||||
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
|
||||||
if ($input->getOption('unset')) {
|
|
||||||
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
|
||||||
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($matches[1] === 'github-oauth') {
|
|
||||||
if (1 !== count($values)) {
|
|
||||||
throw new \RuntimeException('Too many arguments, expected only one token');
|
|
||||||
}
|
|
||||||
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
|
||||||
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
|
|
||||||
} elseif ($matches[1] === 'http-basic') {
|
|
||||||
if (2 !== count($values)) {
|
|
||||||
throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
|
|
||||||
}
|
|
||||||
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
|
||||||
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); };
|
$booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); };
|
||||||
$booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; };
|
$booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; };
|
||||||
|
|
||||||
|
@ -288,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); },
|
||||||
|
@ -298,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; }),
|
||||||
|
@ -311,7 +305,11 @@ 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; }
|
function ($val) { return $val; },
|
||||||
|
),
|
||||||
|
'bin-compat' => array(
|
||||||
|
function ($val) { return in_array($val, array('auto', 'full')); },
|
||||||
|
function ($val) { return $val; },
|
||||||
),
|
),
|
||||||
'discard-changes' => array(
|
'discard-changes' => array(
|
||||||
function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); },
|
function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); },
|
||||||
|
@ -321,9 +319,10 @@ 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; }),
|
||||||
|
'sort-packages' => array($booleanValidator, $booleanNormalizer),
|
||||||
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
|
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
|
||||||
'classmap-authoritative' => array($booleanValidator, $booleanNormalizer),
|
'classmap-authoritative' => array($booleanValidator, $booleanNormalizer),
|
||||||
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
|
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
|
||||||
|
@ -351,7 +350,7 @@ EOT
|
||||||
},
|
},
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
return $vals;
|
return $vals;
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
'github-domains' => array(
|
'github-domains' => array(
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
|
@ -363,7 +362,19 @@ EOT
|
||||||
},
|
},
|
||||||
function ($vals) {
|
function ($vals) {
|
||||||
return $vals;
|
return $vals;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
'gitlab-domains' => array(
|
||||||
|
function ($vals) {
|
||||||
|
if (!is_array($vals)) {
|
||||||
|
return 'array expected';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
function ($vals) {
|
||||||
|
return $vals;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -407,6 +418,64 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle repositories
|
||||||
|
if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
|
||||||
|
if ($input->getOption('unset')) {
|
||||||
|
return $this->configSource->removeRepository($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 === count($values)) {
|
||||||
|
return $this->configSource->addRepository($matches[1], array(
|
||||||
|
'type' => $values[0],
|
||||||
|
'url' => $values[1],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === count($values)) {
|
||||||
|
$bool = strtolower($values[0]);
|
||||||
|
if (true === $booleanValidator($bool) && false === $booleanNormalizer($bool)) {
|
||||||
|
return $this->configSource->addRepository($matches[1], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if (preg_match('/^(github-oauth|gitlab-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
||||||
|
if ($input->getOption('unset')) {
|
||||||
|
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
||||||
|
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($matches[1] === 'github-oauth' || $matches[1] === 'gitlab-oauth') {
|
||||||
|
if (1 !== count($values)) {
|
||||||
|
throw new \RuntimeException('Too many arguments, expected only one token');
|
||||||
|
}
|
||||||
|
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
||||||
|
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
|
||||||
|
} elseif ($matches[1] === 'http-basic') {
|
||||||
|
if (2 !== count($values)) {
|
||||||
|
throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
|
||||||
|
}
|
||||||
|
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
|
||||||
|
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
|
throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,6 +490,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;
|
||||||
|
@ -431,13 +501,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);
|
||||||
|
|
||||||
if (substr_count($k, '.') > 1) {
|
|
||||||
$k = str_split($k, strrpos($k, '.', -2));
|
|
||||||
$k = $k[0] . '.';
|
|
||||||
} else {
|
|
||||||
$k = $origK;
|
$k = $origK;
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -455,9 +519,9 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_string($rawVal) && $rawVal != $value) {
|
if (is_string($rawVal) && $rawVal != $value) {
|
||||||
$output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
|
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
|
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,19 +102,19 @@ 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')) {
|
||||||
$output->writeln('<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,
|
||||||
$input->getArgument('package'),
|
$input->getArgument('package'),
|
||||||
$input->getArgument('directory'),
|
$input->getArgument('directory'),
|
||||||
$input->getArgument('version'),
|
$input->getArgument('version'),
|
||||||
|
@ -128,12 +128,11 @@ EOT
|
||||||
$input->getOption('keep-vcs'),
|
$input->getOption('keep-vcs'),
|
||||||
$input->getOption('no-progress'),
|
$input->getOption('no-progress'),
|
||||||
$input->getOption('no-install'),
|
$input->getOption('no-install'),
|
||||||
$input->getOption('ignore-platform-reqs'),
|
$input->getOption('ignore-platform-reqs')
|
||||||
$input
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, InputInterface $input)
|
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false)
|
||||||
{
|
{
|
||||||
$oldCwd = getcwd();
|
$oldCwd = getcwd();
|
||||||
|
|
||||||
|
@ -147,11 +146,13 @@ 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) {
|
||||||
// dispatch event
|
// dispatch event
|
||||||
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rootPackageConfig = $composer->getConfig();
|
$rootPackageConfig = $composer->getConfig();
|
||||||
|
@ -198,7 +199,7 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$io->write('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
|
$io->writeError('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasVcs = false;
|
$hasVcs = false;
|
||||||
|
@ -219,7 +220,7 @@ EOT
|
||||||
|
|
||||||
if ($noScripts === false) {
|
if ($noScripts === false) {
|
||||||
// dispatch event
|
// dispatch event
|
||||||
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir($oldCwd);
|
chdir($oldCwd);
|
||||||
|
@ -277,9 +278,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, $stability);
|
||||||
|
|
||||||
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."));
|
||||||
|
@ -290,10 +294,20 @@ EOT
|
||||||
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
|
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->write('<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->write('<info>Plugins have been disabled.</info>');
|
$io->writeError('<info>Plugins have been disabled.</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
|
if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
|
||||||
|
@ -309,14 +323,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->write('<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;
|
||||||
}
|
}
|
||||||
|
@ -337,18 +352,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':
|
||||||
|
@ -357,8 +373,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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,14 @@
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
use Composer\DependencyResolver\Pool;
|
use Composer\DependencyResolver\Pool;
|
||||||
|
use Composer\Package\Link;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Repository\ArrayRepository;
|
||||||
|
use Composer\Repository\CompositeRepository;
|
||||||
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -39,6 +45,9 @@ class DependsCommand extends Command
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
|
new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
|
||||||
new InputOption('link-type', '', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Link types to show (require, require-dev)', array_keys($this->linkTypes)),
|
new InputOption('link-type', '', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Link types to show (require, require-dev)', array_keys($this->linkTypes)),
|
||||||
|
new InputOption('match-constraint', 'm', InputOption::VALUE_REQUIRED, 'Filters the dependencies shown using this constraint', '*'),
|
||||||
|
new InputOption('invert-match-constraint', 'i', InputOption::VALUE_NONE, 'Turns --match-constraint around into a blacklist insteead of whitelist'),
|
||||||
|
new InputOption('with-replaces', '', InputOption::VALUE_NONE, 'Search for replaced packages as well'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
Displays detailed information about where a package is referenced.
|
Displays detailed information about where a package is referenced.
|
||||||
|
@ -57,7 +66,12 @@ EOT
|
||||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $input, $output);
|
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $input, $output);
|
||||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||||
|
|
||||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
|
||||||
|
$repo = new CompositeRepository(array(
|
||||||
|
new ArrayRepository(array($composer->getPackage())),
|
||||||
|
$composer->getRepositoryManager()->getLocalRepository(),
|
||||||
|
new PlatformRepository(array(), $platformOverrides),
|
||||||
|
));
|
||||||
$needle = $input->getArgument('package');
|
$needle = $input->getArgument('package');
|
||||||
|
|
||||||
$pool = new Pool();
|
$pool = new Pool();
|
||||||
|
@ -79,12 +93,29 @@ EOT
|
||||||
return $type;
|
return $type;
|
||||||
}, $input->getOption('link-type'));
|
}, $input->getOption('link-type'));
|
||||||
|
|
||||||
|
$versionParser = new VersionParser();
|
||||||
|
$constraint = $versionParser->parseConstraints($input->getOption('match-constraint'));
|
||||||
|
$matchInvert = $input->getOption('invert-match-constraint');
|
||||||
|
|
||||||
|
$needles = array($needle);
|
||||||
|
if (true === $input->getOption('with-replaces')) {
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
$needles = array_merge($needles, array_map(function (Link $link) {
|
||||||
|
return $link->getTarget();
|
||||||
|
}, $package->getReplaces()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
$outputPackages = array();
|
$outputPackages = array();
|
||||||
|
$io = $this->getIO();
|
||||||
|
/** @var PackageInterface $package */
|
||||||
foreach ($repo->getPackages() as $package) {
|
foreach ($repo->getPackages() as $package) {
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
|
/** @var Link $link */
|
||||||
foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
|
foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
|
||||||
if ($link->getTarget() === $needle) {
|
foreach ($needles as $needle) {
|
||||||
|
if ($link->getTarget() === $needle && ($link->getConstraint()->matches($constraint) ? !$matchInvert : $matchInvert)) {
|
||||||
if (!isset($outputPackages[$package->getName()])) {
|
if (!isset($outputPackages[$package->getName()])) {
|
||||||
$messages[] = '<info>'.$package->getPrettyName() . '</info> ' . $linkTypes[$type][1] . ' ' . $needle .' (<info>' . $link->getPrettyConstraint() . '</info>)';
|
$messages[] = '<info>'.$package->getPrettyName() . '</info> ' . $linkTypes[$type][1] . ' ' . $needle .' (<info>' . $link->getPrettyConstraint() . '</info>)';
|
||||||
$outputPackages[$package->getName()] = true;
|
$outputPackages[$package->getName()] = true;
|
||||||
|
@ -93,12 +124,17 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($messages) {
|
if ($messages) {
|
||||||
sort($messages);
|
sort($messages);
|
||||||
$output->writeln($messages);
|
$io->write($messages);
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('<info>There is no installed package depending on "'.$needle.'".</info>');
|
$matchText = '';
|
||||||
|
if ($input->getOption('match-constraint') !== '*') {
|
||||||
|
$matchText = ' in versions '.($matchInvert ? 'not ':'').'matching ' . $input->getOption('match-constraint');
|
||||||
|
}
|
||||||
|
$io->writeError('<info>There is no installed package depending on "'.$needle.'"'.$matchText.'.</info>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,13 @@ use Symfony\Component\Console\Input\InputOption;
|
||||||
*/
|
*/
|
||||||
class DiagnoseCommand extends Command
|
class DiagnoseCommand extends Command
|
||||||
{
|
{
|
||||||
|
/** @var RemoteFileSystem */
|
||||||
protected $rfs;
|
protected $rfs;
|
||||||
|
|
||||||
|
/** @var ProcessExecutor */
|
||||||
protected $process;
|
protected $process;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
protected $failures = 0;
|
protected $failures = 0;
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
|
@ -51,15 +56,20 @@ 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);
|
||||||
|
|
||||||
$output->write('Checking composer.json: ');
|
$io->write('Checking composer.json: ', false);
|
||||||
$this->outputResult($output, $this->checkComposerSchema());
|
$this->outputResult($this->checkComposerSchema());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
|
@ -68,40 +78,67 @@ EOT
|
||||||
$config = Factory::createConfig();
|
$config = Factory::createConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->rfs = new RemoteFilesystem($this->getIO(), $config);
|
$this->rfs = Factory::createRemoteFilesystem($io, $config);
|
||||||
$this->process = new ProcessExecutor($this->getIO());
|
$this->process = new ProcessExecutor($io);
|
||||||
|
|
||||||
$output->write('Checking platform settings: ');
|
$io->write('Checking platform settings: ', false);
|
||||||
$this->outputResult($output, $this->checkPlatform());
|
$this->outputResult($this->checkPlatform());
|
||||||
|
|
||||||
$output->write('Checking git settings: ');
|
$io->write('Checking git settings: ', false);
|
||||||
$this->outputResult($output, $this->checkGit());
|
$this->outputResult($this->checkGit());
|
||||||
|
|
||||||
$output->write('Checking http connectivity: ');
|
$io->write('Checking http connectivity to packagist: ', false);
|
||||||
$this->outputResult($output, $this->checkHttp($config));
|
$this->outputResult($this->checkHttp('http', $config));
|
||||||
|
|
||||||
|
$io->write('Checking https connectivity to packagist: ', false);
|
||||||
|
$this->outputResult($this->checkHttp('https', $config));
|
||||||
|
|
||||||
$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'])) {
|
||||||
$output->write('Checking HTTP proxy: ');
|
$io->write('Checking HTTP proxy: ', false);
|
||||||
$this->outputResult($output, $this->checkHttpProxy());
|
$this->outputResult($this->checkHttpProxy());
|
||||||
$output->write('Checking HTTP proxy support for request_fulluri: ');
|
$io->write('Checking HTTP proxy support for request_fulluri: ', false);
|
||||||
$this->outputResult($output, $this->checkHttpProxyFullUriRequestParam());
|
$this->outputResult($this->checkHttpProxyFullUriRequestParam());
|
||||||
$output->write('Checking HTTPS proxy support for request_fulluri: ');
|
$io->write('Checking HTTPS proxy support for request_fulluri: ', false);
|
||||||
$this->outputResult($output, $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) {
|
||||||
$output->write('Checking '.$domain.' oauth access: ');
|
$io->write('Checking '.$domain.' oauth access: ', false);
|
||||||
$this->outputResult($output, $this->checkGithubOauth($domain, $token));
|
$this->outputResult($this->checkGithubOauth($domain, $token));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$io->write('Checking github.com rate limit: ', false);
|
||||||
|
try {
|
||||||
|
$rate = $this->getGithubRateLimit('github.com');
|
||||||
|
$this->outputResult(true);
|
||||||
|
if (10 > $rate['remaining']) {
|
||||||
|
$io->write('<warning>WARNING</warning>');
|
||||||
|
$io->write(sprintf(
|
||||||
|
'<comment>Github has a rate limit on their API. '
|
||||||
|
. 'You currently have <options=bold>%u</options=bold> '
|
||||||
|
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
|
||||||
|
. 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL
|
||||||
|
. ' https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens</comment>',
|
||||||
|
$rate['remaining'],
|
||||||
|
$rate['limit']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->write('Checking disk free space: ');
|
$io->write('Checking disk free space: ', false);
|
||||||
$this->outputResult($output, $this->checkDiskSpace($config));
|
$this->outputResult($this->checkDiskSpace($config));
|
||||||
|
|
||||||
$output->write('Checking composer version: ');
|
$io->write('Checking composer version: ', false);
|
||||||
$this->outputResult($output, $this->checkVersion());
|
$this->outputResult($this->checkVersion());
|
||||||
|
|
||||||
return $this->failures;
|
return $this->failures;
|
||||||
}
|
}
|
||||||
|
@ -109,11 +146,11 @@ EOT
|
||||||
private function checkComposerSchema()
|
private function checkComposerSchema()
|
||||||
{
|
{
|
||||||
$validator = new ConfigValidator($this->getIO());
|
$validator = new ConfigValidator($this->getIO());
|
||||||
list($errors, $publishErrors, $warnings) = $validator->validate(Factory::getComposerFile());
|
list($errors, , $warnings) = $validator->validate(Factory::getComposerFile());
|
||||||
|
|
||||||
if ($errors || $publishErrors || $warnings) {
|
if ($errors || $warnings) {
|
||||||
$messages = array(
|
$messages = array(
|
||||||
'error' => array_merge($errors, $publishErrors),
|
'error' => $errors,
|
||||||
'warning' => $warnings,
|
'warning' => $warnings,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -134,44 +171,35 @@ EOT
|
||||||
{
|
{
|
||||||
$this->process->execute('git config color.ui', $output);
|
$this->process->execute('git config color.ui', $output);
|
||||||
if (strtolower(trim($output)) === 'always') {
|
if (strtolower(trim($output)) === 'always') {
|
||||||
return '<warning>Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.</warning>';
|
return '<comment>Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.</comment>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkHttp(Config $config)
|
private function checkHttp($proto, Config $config)
|
||||||
{
|
{
|
||||||
$disableTls = false;
|
$disableTls = false;
|
||||||
$result = array();
|
$result = array();
|
||||||
if($config->get('disable-tls') === true) {
|
if ($proto === 'https' && $config->get('disable-tls') === true) {
|
||||||
$protocol = 'http';
|
|
||||||
$disableTls = true;
|
$disableTls = true;
|
||||||
$result[] = '<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>';
|
$result[] = '<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>';
|
||||||
} else {
|
|
||||||
$protocol = 'https';
|
|
||||||
}
|
}
|
||||||
if (!extension_loaded('openssl') && !$disableTls) {
|
if ($proto === 'https' && !extension_loaded('openssl') && !$disableTls) {
|
||||||
$result[] = '<error>Composer is configured to use SSL/TLS protection but the openssl extension is not available.</error>';
|
$result[] = '<error>Composer is configured to use SSL/TLS protection but the openssl extension is not available.</error>';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->rfs = Factory::createRemoteFilesystem($this->getIO(), $config);
|
$this->rfs->getContents('packagist.org', $proto . '://packagist.org/packages.json', false);
|
||||||
} catch (TransportException $e) {
|
} catch (TransportException $e) {
|
||||||
if (preg_match('|cafile|', $e->getMessage())) {
|
if (preg_match('|cafile|', $e->getMessage())) {
|
||||||
$result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
|
$result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
|
||||||
$result[] = '<error>Unable to locate a valid CA certificate file. You must set a valid \'cafile\' option.</error>';
|
$result[] = '<error>Unable to locate a valid CA certificate file. You must set a valid \'cafile\' option.</error>';
|
||||||
$result[] = '<error>You can alternatively disable this error, at your own risk, by enabling the \'disable-tls\' option.</error>';
|
$result[] = '<error>You can alternatively disable this error, at your own risk, by enabling the \'disable-tls\' option.</error>';
|
||||||
} else {
|
} else {
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$json = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
array_unshift($result, '[' . get_class($e) . '] ' . $e->getMessage());
|
array_unshift($result, '[' . get_class($e) . '] ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (count($result) > 0) {
|
if (count($result) > 0) {
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -240,7 +268,7 @@ EOT
|
||||||
|
|
||||||
$url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0';
|
$url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0';
|
||||||
try {
|
try {
|
||||||
$rfcResult = $this->rfs->getContents('github.com', $url, false);
|
$this->rfs->getContents('github.com', $url, false);
|
||||||
} catch (TransportException $e) {
|
} catch (TransportException $e) {
|
||||||
try {
|
try {
|
||||||
$this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false)));
|
$this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false)));
|
||||||
|
@ -260,16 +288,37 @@ EOT
|
||||||
try {
|
try {
|
||||||
$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) ? true : 'Unexpected error';
|
return $this->rfs->getContents($domain, $url, false, array(
|
||||||
|
'retry-auth-failure' => false,
|
||||||
|
)) ? true : 'Unexpected error';
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($e instanceof TransportException && $e->getCode() === 401) {
|
if ($e instanceof TransportException && $e->getCode() === 401) {
|
||||||
return '<warning>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</warning>';
|
return '<comment>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</comment>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $domain
|
||||||
|
* @param string $token
|
||||||
|
* @throws TransportException
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getGithubRateLimit($domain, $token = null)
|
||||||
|
{
|
||||||
|
if ($token) {
|
||||||
|
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
|
||||||
|
$json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false));
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
|
||||||
|
return $data['resources']['core'];
|
||||||
|
}
|
||||||
|
|
||||||
private function checkDiskSpace($config)
|
private function checkDiskSpace($config)
|
||||||
{
|
{
|
||||||
$minSpaceFree = 1024 * 1024;
|
$minSpaceFree = 1024 * 1024;
|
||||||
|
@ -288,28 +337,32 @@ EOT
|
||||||
$latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
|
$latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
|
||||||
|
|
||||||
if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') {
|
if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') {
|
||||||
return '<warning>You are not running the latest version</warning>';
|
return '<comment>You are not running the latest version, run `composer self-update` to update</comment>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function outputResult(OutputInterface $output, $result)
|
/**
|
||||||
|
* @param bool|string|\Exception $result
|
||||||
|
*/
|
||||||
|
private function outputResult($result)
|
||||||
{
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
if (true === $result) {
|
if (true === $result) {
|
||||||
$output->writeln('<info>OK</info>');
|
$io->write('<info>OK</info>');
|
||||||
} else {
|
} else {
|
||||||
$this->failures++;
|
$this->failures++;
|
||||||
$output->writeln('<error>FAIL</error>');
|
$io->write('<error>FAIL</error>');
|
||||||
if ($result instanceof \Exception) {
|
if ($result instanceof \Exception) {
|
||||||
$output->writeln('['.get_class($result).'] '.$result->getMessage());
|
$io->write('['.get_class($result).'] '.$result->getMessage());
|
||||||
} elseif ($result) {
|
} elseif ($result) {
|
||||||
if (is_array($result)) {
|
if (is_array($result)) {
|
||||||
foreach ($result as $message) {
|
foreach ($result as $message) {
|
||||||
$output->writeln($message);
|
$io->write($message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$output->writeln($result);
|
$io->write($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +372,7 @@ EOT
|
||||||
{
|
{
|
||||||
$output = '';
|
$output = '';
|
||||||
$out = function ($msg, $style) use (&$output) {
|
$out = function ($msg, $style) use (&$output) {
|
||||||
$output .= '<'.$style.'>'.$msg.'</'.$style.'>';
|
$output .= '<'.$style.'>'.$msg.'</'.$style.'>'.PHP_EOL;
|
||||||
};
|
};
|
||||||
|
|
||||||
// code below taken from getcomposer.org/installer, any changes should be made there and replicated here
|
// code below taken from getcomposer.org/installer, any changes should be made there and replicated here
|
||||||
|
@ -335,32 +388,46 @@ EOT
|
||||||
}
|
}
|
||||||
$iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
|
$iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
|
||||||
|
|
||||||
|
if (!function_exists('json_decode')) {
|
||||||
|
$errors['json'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extension_loaded('Phar')) {
|
||||||
|
$errors['phar'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extension_loaded('filter')) {
|
||||||
|
$errors['filter'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extension_loaded('hash')) {
|
||||||
|
$errors['hash'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ini_get('allow_url_fopen')) {
|
if (!ini_get('allow_url_fopen')) {
|
||||||
$errors['allow_url_fopen'] = true;
|
$errors['allow_url_fopen'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
|
if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
|
||||||
|
$errors['ioncube'] = ioncube_loader_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extension_loaded('openssl')) {
|
if (!extension_loaded('openssl')) {
|
||||||
$warnings['openssl'] = true;
|
$errors['openssl'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
|
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
|
||||||
$warnings['apc_cli'] = true;
|
$warnings['apc_cli'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ini_get('xdebug.profiler_enabled')) {
|
|
||||||
$warnings['xdebug_profile'] = true;
|
|
||||||
} elseif (extension_loaded('xdebug')) {
|
|
||||||
$warnings['xdebug_loaded'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
phpinfo(INFO_GENERAL);
|
phpinfo(INFO_GENERAL);
|
||||||
$phpinfo = ob_get_clean();
|
$phpinfo = ob_get_clean();
|
||||||
|
@ -376,9 +443,49 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ini_get('xdebug.profiler_enabled')) {
|
||||||
|
$warnings['xdebug_profile'] = true;
|
||||||
|
} elseif (extension_loaded('xdebug')) {
|
||||||
|
$warnings['xdebug_loaded'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($errors)) {
|
if (!empty($errors)) {
|
||||||
foreach ($errors as $error => $current) {
|
foreach ($errors as $error => $current) {
|
||||||
switch ($error) {
|
switch ($error) {
|
||||||
|
case 'json':
|
||||||
|
$text = PHP_EOL."The json extension is missing.".PHP_EOL;
|
||||||
|
$text .= "Install it or recompile php without --disable-json";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'phar':
|
||||||
|
$text = PHP_EOL."The phar extension is missing.".PHP_EOL;
|
||||||
|
$text .= "Install it or recompile php without --disable-phar";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'filter':
|
||||||
|
$text = PHP_EOL."The filter extension is missing.".PHP_EOL;
|
||||||
|
$text .= "Install it or recompile php without --disable-filter";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'hash':
|
||||||
|
$text = PHP_EOL."The hash extension is missing.".PHP_EOL;
|
||||||
|
$text .= "Install it or recompile php without --disable-hash";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unicode':
|
||||||
|
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
|
||||||
|
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
|
||||||
|
$text .= " detect_unicode = Off";
|
||||||
|
$displayIniMessage = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'suhosin':
|
||||||
|
$text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL;
|
||||||
|
$text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL;
|
||||||
|
$text .= " suhosin.executor.include.whitelist = phar ".$current;
|
||||||
|
$displayIniMessage = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'php':
|
case 'php':
|
||||||
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
|
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
|
||||||
break;
|
break;
|
||||||
|
@ -389,6 +496,18 @@ EOT
|
||||||
$text .= " allow_url_fopen = On";
|
$text .= " allow_url_fopen = On";
|
||||||
$displayIniMessage = true;
|
$displayIniMessage = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'ioncube':
|
||||||
|
$text = PHP_EOL."Your ionCube Loader extension ($current) is incompatible with Phar files.".PHP_EOL;
|
||||||
|
$text .= "Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:".PHP_EOL;
|
||||||
|
$text .= " zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so";
|
||||||
|
$displayIniMessage = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'openssl':
|
||||||
|
$text = PHP_EOL."The openssl extension is missing, which means that secure HTTPS transfers are impossible.".PHP_EOL;
|
||||||
|
$text .= "If possible you should enable it or recompile php with --with-openssl";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$out($text, 'error');
|
$out($text, 'error');
|
||||||
}
|
}
|
||||||
|
@ -400,51 +519,46 @@ EOT
|
||||||
foreach ($warnings as $warning => $current) {
|
foreach ($warnings as $warning => $current) {
|
||||||
switch ($warning) {
|
switch ($warning) {
|
||||||
case 'apc_cli':
|
case 'apc_cli':
|
||||||
$text = PHP_EOL."The apc.enable_cli setting is incorrect.".PHP_EOL;
|
$text = "The apc.enable_cli setting is incorrect.".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;
|
||||||
$text .= " apc.enable_cli = Off";
|
$text .= " apc.enable_cli = Off";
|
||||||
$displayIniMessage = true;
|
$displayIniMessage = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sigchild':
|
case 'sigchild':
|
||||||
$text = PHP_EOL."PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL;
|
$text = "PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL;
|
||||||
$text .= "Recompile it without this flag if possible, see also:".PHP_EOL;
|
$text .= "Recompile it without this flag if possible, see also:".PHP_EOL;
|
||||||
$text .= " https://bugs.php.net/bug.php?id=22999";
|
$text .= " https://bugs.php.net/bug.php?id=22999";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'curlwrappers':
|
case 'curlwrappers':
|
||||||
$text = PHP_EOL."PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL;
|
$text = "PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL;
|
||||||
$text .= " Recompile it without this flag if possible";
|
$text .= " Recompile it without this flag if possible";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'openssl':
|
|
||||||
$text = PHP_EOL."The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL;
|
|
||||||
$text .= "If possible you should enable it or recompile php with --with-openssl";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'php':
|
case 'php':
|
||||||
$text = PHP_EOL."Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
|
$text = "Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
|
||||||
$text .= " Composer works with 5.3.2+ for most people, but there might be edge case issues.";
|
$text .= " Composer works with 5.3.2+ for most people, but there might be edge case issues.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'xdebug_loaded':
|
case 'xdebug_loaded':
|
||||||
$text = PHP_EOL."The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
|
$text = "The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
|
||||||
$text .= "Disabling it when using Composer is recommended, but should not cause issues beyond slowness.";
|
$text .= " Disabling it when using Composer is recommended.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'xdebug_profile':
|
case 'xdebug_profile':
|
||||||
$text = PHP_EOL."The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL;
|
$text = "The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL;
|
||||||
$text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL;
|
$text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL;
|
||||||
$text .= " xdebug.profiler_enabled = 0";
|
$text .= " xdebug.profiler_enabled = 0";
|
||||||
$displayIniMessage = true;
|
$displayIniMessage = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$out($text, 'warning');
|
$out($text, 'comment');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($displayIniMessage) {
|
if ($displayIniMessage) {
|
||||||
$out($iniMessage, 'warning');
|
$out($iniMessage, 'comment');
|
||||||
}
|
}
|
||||||
|
|
||||||
return !$warnings && !$errors ? true : $output;
|
return !$warnings && !$errors ? true : $output;
|
||||||
|
|
|
@ -30,7 +30,9 @@ class DumpAutoloadCommand extends Command
|
||||||
->setAliases(array('dumpautoload'))
|
->setAliases(array('dumpautoload'))
|
||||||
->setDescription('Dumps the autoloader')
|
->setDescription('Dumps the autoloader')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
|
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||||
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,16 +54,19 @@ 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) {
|
||||||
$output->writeln('<info>Generating optimized autoload files</info>');
|
$this->getIO()->writeError('<info>Generating optimized autoload files</info>');
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('<info>Generating autoload files</info>');
|
$this->getIO()->writeError('<info>Generating autoload files</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
$generator = $composer->getAutoloadGenerator();
|
$generator = $composer->getAutoloadGenerator();
|
||||||
$generator->setDevMode(!$input->getOption('no-dev'));
|
$generator->setDevMode(!$input->getOption('no-dev'));
|
||||||
|
$generator->setClassMapAuthoritative($authoritative);
|
||||||
|
$generator->setRunScripts(!$input->getOption('no-scripts'));
|
||||||
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ EOT
|
||||||
// change to global dir
|
// change to global dir
|
||||||
$config = Factory::createConfig();
|
$config = Factory::createConfig();
|
||||||
chdir($config->get('home'));
|
chdir($config->get('home'));
|
||||||
$output->writeln('<info>Changed current directory to '.$config->get('home').'</info>');
|
$this->getIO()->writeError('<info>Changed current directory to '.$config->get('home').'</info>');
|
||||||
|
|
||||||
// create new input without "global" command prefix
|
// create new input without "global" command prefix
|
||||||
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
|
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -57,65 +56,56 @@ EOT
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$repo = $this->initializeRepo();
|
$repos = $this->initializeRepos();
|
||||||
|
$io = $this->getIO();
|
||||||
$return = 0;
|
$return = 0;
|
||||||
|
|
||||||
foreach ($input->getArgument('packages') as $packageName) {
|
foreach ($input->getArgument('packages') as $packageName) {
|
||||||
$package = $this->getPackage($repo, $packageName);
|
$handled = false;
|
||||||
|
$packageExists = false;
|
||||||
|
foreach ($repos as $repo) {
|
||||||
|
foreach ($repo->findPackages($packageName) as $package) {
|
||||||
|
$packageExists = true;
|
||||||
|
if ($this->handlePackage($package, $input->getOption('homepage'), $input->getOption('show'))) {
|
||||||
|
$handled = true;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$package instanceof CompletePackageInterface) {
|
if (!$packageExists) {
|
||||||
$return = 1;
|
$return = 1;
|
||||||
$output->writeln('<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;
|
||||||
$output->writeln('<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')) {
|
|
||||||
$output->writeln(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,26 +129,31 @@ EOT
|
||||||
} elseif (0 === $osx) {
|
} elseif (0 === $osx) {
|
||||||
passthru('open ' . $url);
|
passthru('open ' . $url);
|
||||||
} else {
|
} else {
|
||||||
$this->getIO()->write('no suitable browser opening command found, open yourself: ' . $url);
|
$this->getIO()->writeError('no suitable browser opening command found, open yourself: ' . $url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the repo
|
* Initializes repositories
|
||||||
*
|
*
|
||||||
* @return CompositeRepository
|
* Returns an array of repos in order they should be checked in
|
||||||
|
*
|
||||||
|
* @return RepositoryInterface[]
|
||||||
*/
|
*/
|
||||||
private function initializeRepo()
|
private function initializeRepos()
|
||||||
{
|
{
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
|
|
||||||
if ($composer) {
|
if ($composer) {
|
||||||
$repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
|
return array_merge(
|
||||||
} else {
|
array(new ArrayRepository(array($composer->getPackage()))), // root package
|
||||||
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
array($composer->getRepositoryManager()->getLocalRepository()), // installed packages
|
||||||
$repo = new CompositeRepository($defaultRepos);
|
$composer->getRepositoryManager()->getRepositories() // remotes
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $repo;
|
$defaultRepos = Factory::createDefaultRepositories($this->getIO());
|
||||||
|
|
||||||
|
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()) {
|
||||||
$output->writeln(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)) {
|
|
||||||
$output->writeln('<error>Command aborted</error>');
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -141,27 +129,30 @@ 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');
|
||||||
$output->writeln(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
|
||||||
$output->writeln(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);
|
||||||
|
|
||||||
$output->writeln(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;
|
||||||
|
|
||||||
$output->writeln(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) {
|
||||||
$output->writeln(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),
|
||||||
''
|
'',
|
||||||
));
|
));
|
||||||
|
|
||||||
$output->writeln($choices);
|
$io->writeError($choices);
|
||||||
$output->writeln('');
|
$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];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($matches[(int) $selection])) {
|
|
||||||
throw new \Exception('Not a valid selection');
|
|
||||||
}
|
|
||||||
|
|
||||||
$package = $matches[(int) $selection];
|
$package = $matches[(int) $selection];
|
||||||
|
|
||||||
return $package['name'];
|
return $package['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('{^\s*(?P<name>[\S/]+)(?:\s+(?P<version>\S+))?\s*$}', $selection, $packageMatches)) {
|
||||||
|
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'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \Exception('Not a valid selection');
|
||||||
};
|
};
|
||||||
|
|
||||||
$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);
|
|
||||||
|
|
||||||
$output->writeln(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(
|
||||||
|
|
|
@ -48,6 +48,7 @@ class InstallCommand extends Command
|
||||||
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('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
new InputOption('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
||||||
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
||||||
|
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.'),
|
||||||
))
|
))
|
||||||
|
@ -66,20 +67,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')) {
|
||||||
$output->writeln('<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')) {
|
||||||
$output->writeln('<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')) {
|
||||||
|
$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);
|
||||||
|
@ -108,7 +113,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'))
|
||||||
|
@ -119,6 +125,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,12 +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\TableHelper;
|
use Symfony\Component\Console\Helper\Table;
|
||||||
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;
|
||||||
|
@ -56,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 {
|
||||||
|
@ -65,38 +62,43 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($packages);
|
ksort($packages);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
switch ($format = $input->getOption('format')) {
|
switch ($format = $input->getOption('format')) {
|
||||||
case 'text':
|
case 'text':
|
||||||
$output->writeln('Name: <comment>'.$root->getPrettyName().'</comment>');
|
$io->write('Name: <comment>'.$root->getPrettyName().'</comment>');
|
||||||
$output->writeln('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
|
$io->write('Version: <comment>'.$root->getFullPrettyVersion().'</comment>');
|
||||||
$output->writeln('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
|
$io->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
|
||||||
$output->writeln('Dependencies:');
|
$io->write('Dependencies:');
|
||||||
|
$io->write('');
|
||||||
|
|
||||||
$table = $this->getHelperSet()->get('table');
|
$table = new Table($output);
|
||||||
$table->setLayout(TableHelper::LAYOUT_BORDERLESS);
|
$table->setStyle('compact');
|
||||||
$table->setHorizontalBorderChar('');
|
$table->getStyle()->setVerticalBorderChar('');
|
||||||
|
$table->getStyle()->setCellRowContentFormat('%s ');
|
||||||
|
$table->setHeaders(array('Name', 'Version', 'License'));
|
||||||
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',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
$table->render($output);
|
$table->render();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'json':
|
case 'json':
|
||||||
|
$dependencies = array();
|
||||||
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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln(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])) {
|
||||||
$output->writeln('<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 {
|
||||||
$output->writeln('<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) {
|
||||||
$output->writeln("\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;
|
||||||
|
@ -50,6 +50,8 @@ class RequireCommand extends InitCommand
|
||||||
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
||||||
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.
|
||||||
|
@ -66,24 +68,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")) {
|
||||||
$output->writeln('<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)) {
|
||||||
$output->writeln('<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)) {
|
||||||
$output->writeln('<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());
|
||||||
|
@ -91,12 +98,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';
|
||||||
|
@ -109,7 +119,7 @@ EOT
|
||||||
$versionParser->parseConstraints($constraint);
|
$versionParser->parseConstraints($constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sortPackages = $input->getOption('sort-packages');
|
$sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages');
|
||||||
|
|
||||||
if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) {
|
if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) {
|
||||||
foreach ($requirements as $package => $version) {
|
foreach ($requirements as $package => $version) {
|
||||||
|
@ -124,18 +134,19 @@ EOT
|
||||||
$json->write($composerDefinition);
|
$json->write($composerDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln('<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);
|
||||||
|
@ -147,6 +158,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'))
|
||||||
|
@ -156,10 +169,10 @@ EOT
|
||||||
$status = $install->run();
|
$status = $install->run();
|
||||||
if ($status !== 0) {
|
if ($status !== 0) {
|
||||||
if ($newlyCreated) {
|
if ($newlyCreated) {
|
||||||
$output->writeln("\n".'<error>Installation failed, deleting '.$file.'.</error>');
|
$io->writeError("\n".'<error>Installation failed, deleting '.$file.'.</error>');
|
||||||
unlink($json->getPath());
|
unlink($json->getPath());
|
||||||
} else {
|
} else {
|
||||||
$output->writeln("\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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class RunScriptCommand extends Command
|
||||||
/**
|
/**
|
||||||
* @var array Array with command events
|
* @var array Array with command events
|
||||||
*/
|
*/
|
||||||
protected $commandEvents = array(
|
protected $scriptEvents = array(
|
||||||
ScriptEvents::PRE_INSTALL_CMD,
|
ScriptEvents::PRE_INSTALL_CMD,
|
||||||
ScriptEvents::POST_INSTALL_CMD,
|
ScriptEvents::POST_INSTALL_CMD,
|
||||||
ScriptEvents::PRE_UPDATE_CMD,
|
ScriptEvents::PRE_UPDATE_CMD,
|
||||||
|
@ -35,17 +35,11 @@ class RunScriptCommand extends Command
|
||||||
ScriptEvents::PRE_STATUS_CMD,
|
ScriptEvents::PRE_STATUS_CMD,
|
||||||
ScriptEvents::POST_STATUS_CMD,
|
ScriptEvents::POST_STATUS_CMD,
|
||||||
ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
|
ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
|
||||||
ScriptEvents::POST_CREATE_PROJECT_CMD
|
ScriptEvents::POST_CREATE_PROJECT_CMD,
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array Array with script events
|
|
||||||
*/
|
|
||||||
protected $scriptEvents = array(
|
|
||||||
ScriptEvents::PRE_ARCHIVE_CMD,
|
ScriptEvents::PRE_ARCHIVE_CMD,
|
||||||
ScriptEvents::POST_ARCHIVE_CMD,
|
ScriptEvents::POST_ARCHIVE_CMD,
|
||||||
ScriptEvents::PRE_AUTOLOAD_DUMP,
|
ScriptEvents::PRE_AUTOLOAD_DUMP,
|
||||||
ScriptEvents::POST_AUTOLOAD_DUMP
|
ScriptEvents::POST_AUTOLOAD_DUMP,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
|
@ -54,10 +48,11 @@ class RunScriptCommand extends Command
|
||||||
->setName('run-script')
|
->setName('run-script')
|
||||||
->setDescription('Run the scripts defined in composer.json.')
|
->setDescription('Run the scripts defined in composer.json.')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'),
|
new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.'),
|
||||||
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
|
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
|
||||||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
|
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
|
||||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
|
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
|
||||||
|
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List scripts.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The <info>run-script</info> command runs scripts defined in composer.json:
|
The <info>run-script</info> command runs scripts defined in composer.json:
|
||||||
|
@ -70,8 +65,14 @@ EOT
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
if ($input->getOption('list')) {
|
||||||
|
return $this->listScripts();
|
||||||
|
} elseif (!$input->getArgument('script')) {
|
||||||
|
throw new \RunTimeException('Missing required argument "script"');
|
||||||
|
}
|
||||||
|
|
||||||
$script = $input->getArgument('script');
|
$script = $input->getArgument('script');
|
||||||
if (!in_array($script, $this->commandEvents) && !in_array($script, $this->scriptEvents)) {
|
if (!in_array($script, $this->scriptEvents)) {
|
||||||
if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
|
if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
|
||||||
throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script));
|
throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script));
|
||||||
}
|
}
|
||||||
|
@ -86,15 +87,29 @@ 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');
|
||||||
|
|
||||||
if (in_array($script, $this->commandEvents)) {
|
|
||||||
return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
|
return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function listScripts()
|
||||||
|
{
|
||||||
|
$scripts = $this->getComposer()->getPackage()->getScripts();
|
||||||
|
|
||||||
|
if (!count($scripts)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io = $this->getIO();
|
||||||
|
$io->writeError('<info>scripts:</info>');
|
||||||
|
foreach ($scripts as $name => $script) {
|
||||||
|
$io->write(' ' . $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
|
@ -58,13 +58,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);
|
||||||
$output->writeln('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));
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,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) {
|
||||||
$output->writeln($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
|
$io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,8 @@ EOT
|
||||||
} else {
|
} else {
|
||||||
$baseUrl = 'https://' . self::HOMEPAGE;
|
$baseUrl = 'https://' . self::HOMEPAGE;
|
||||||
}
|
}
|
||||||
$remoteFilesystem = Factory::createRemoteFilesystem($this->getIO(), $config);
|
$io = $this->getIO();
|
||||||
|
$remoteFilesystem = Factory::createRemoteFilesystem($io, $config);
|
||||||
|
|
||||||
// TODO: Silent switch probably should be kicking out exception
|
// TODO: Silent switch probably should be kicking out exception
|
||||||
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
||||||
|
@ -96,13 +97,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) {
|
||||||
$output->writeln('<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) {
|
||||||
$output->writeln('<info>You are already using composer version '.$updateVersion.'.</info>');
|
$io->writeError('<info>You are already using composer version '.$updateVersion.'.</info>');
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -116,11 +117,11 @@ EOT
|
||||||
self::OLD_INSTALL_EXT
|
self::OLD_INSTALL_EXT
|
||||||
);
|
);
|
||||||
|
|
||||||
$output->writeln(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)) {
|
||||||
$output->writeln('<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;
|
||||||
}
|
}
|
||||||
|
@ -132,22 +133,22 @@ EOT
|
||||||
$fs = new Filesystem;
|
$fs = new Filesystem;
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
$file = (string) $file;
|
$file = (string) $file;
|
||||||
$output->writeln('<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)) {
|
||||||
$output->writeln('<error>The file is corrupted ('.$err->getMessage().').</error>');
|
$io->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
|
||||||
$output->writeln('<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)) {
|
||||||
$output->writeln('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 {
|
||||||
$output->writeln('<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>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +173,10 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
|
$oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
|
||||||
$output->writeln(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)) {
|
||||||
$output->writeln('<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;
|
||||||
}
|
}
|
||||||
|
@ -182,6 +184,11 @@ EOT
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $localFilename
|
||||||
|
* @param string $newFilename
|
||||||
|
* @param string $backupTarget
|
||||||
|
*/
|
||||||
protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
|
protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -16,9 +16,11 @@ 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 Composer\Package\PackageInterface;
|
||||||
|
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -28,14 +30,17 @@ 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>
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @author Jérémy Romey <jeremyFreeAgent>
|
||||||
*/
|
*/
|
||||||
class ShowCommand extends Command
|
class ShowCommand extends Command
|
||||||
{
|
{
|
||||||
protected $versionParser;
|
protected $versionParser;
|
||||||
|
protected $colors;
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
|
@ -54,6 +59,7 @@ class ShowCommand extends Command
|
||||||
new InputOption('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
new InputOption('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
||||||
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
||||||
new InputOption('path', 'P', InputOption::VALUE_NONE, 'Show package paths'),
|
new InputOption('path', 'P', InputOption::VALUE_NONE, 'Show package paths'),
|
||||||
|
new InputOption('tree', 't', InputOption::VALUE_NONE, 'List the dependencies as a tree'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The show command displays detailed information about a package, or
|
The show command displays detailed information about a package, or
|
||||||
|
@ -67,11 +73,25 @@ EOT
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$this->versionParser = new VersionParser;
|
$this->versionParser = new VersionParser;
|
||||||
|
if ($input->getOption('tree')) {
|
||||||
// init repos
|
$this->initStyles($output);
|
||||||
$platformRepo = new PlatformRepository;
|
}
|
||||||
|
|
||||||
$composer = $this->getComposer(false);
|
$composer = $this->getComposer(false);
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
|
if ($input->getOption('tree') && !$input->getOption('installed')) {
|
||||||
|
$io->writeError('The --tree (-t) option is only usable in combination with --installed (-i) or by passing a single package name to show, assuming -i');
|
||||||
|
$input->setOption('installed', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init repos
|
||||||
|
$platformOverrides = array();
|
||||||
|
if ($composer) {
|
||||||
|
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
|
||||||
|
}
|
||||||
|
$platformRepo = new PlatformRepository(array(), $platformOverrides);
|
||||||
|
|
||||||
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));
|
||||||
|
@ -84,17 +104,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);
|
||||||
$output->writeln('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);
|
||||||
$output->writeln('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));
|
||||||
}
|
}
|
||||||
|
@ -117,24 +137,42 @@ EOT
|
||||||
$versions = array($package->getPrettyVersion() => $package->getVersion());
|
$versions = array($package->getPrettyVersion() => $package->getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->printMeta($input, $output, $package, $versions, $installedRepo, $repos);
|
if ($input->getOption('tree')) {
|
||||||
$this->printLinks($input, $output, $package, 'requires');
|
$this->displayPackageTree($package, $installedRepo, $repos, $output);
|
||||||
$this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
|
} else {
|
||||||
|
$this->printMeta($package, $versions, $installedRepo, $repos);
|
||||||
|
$this->printLinks($package, 'requires');
|
||||||
|
$this->printLinks($package, 'devRequires', 'requires (dev)');
|
||||||
if ($package->getSuggests()) {
|
if ($package->getSuggests()) {
|
||||||
$output->writeln("\n<info>suggests</info>");
|
$io->write("\n<info>suggests</info>");
|
||||||
foreach ($package->getSuggests() as $suggested => $reason) {
|
foreach ($package->getSuggests() as $suggested => $reason) {
|
||||||
$output->writeln($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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// list packages
|
// show tree view if requested
|
||||||
$packages = array();
|
if ($input->getOption('tree')) {
|
||||||
|
$rootPackage = $this->getComposer()->getPackage();
|
||||||
|
$rootRequires = array_map(
|
||||||
|
'strtolower',
|
||||||
|
array_keys(array_merge($rootPackage->getRequires(), $rootPackage->getDevRequires()))
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($installedRepo->getPackages() as $package) {
|
||||||
|
if (in_array($package->getName(), $rootRequires, true)) {
|
||||||
|
$this->displayPackageTree($package, $installedRepo, $repos, $output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ($repos instanceof CompositeRepository) {
|
if ($repos instanceof CompositeRepository) {
|
||||||
$repos = $repos->getRepositories();
|
$repos = $repos->getRepositories();
|
||||||
|
@ -142,6 +180,9 @@ EOT
|
||||||
$repos = array($repos);
|
$repos = array($repos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// list packages
|
||||||
|
$packages = array();
|
||||||
|
|
||||||
foreach ($repos as $repo) {
|
foreach ($repos as $repo) {
|
||||||
if ($repo === $platformRepo) {
|
if ($repo === $platformRepo) {
|
||||||
$type = '<info>platform</info>:';
|
$type = '<info>platform</info>:';
|
||||||
|
@ -169,12 +210,12 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$tree = !$input->getOption('platform') && !$input->getOption('installed') && !$input->getOption('available');
|
$showAllTypes = !$input->getOption('platform') && !$input->getOption('installed') && !$input->getOption('available');
|
||||||
$indent = $tree ? ' ' : '';
|
$indent = $showAllTypes ? ' ' : '';
|
||||||
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 ($showAllTypes) {
|
||||||
$output->writeln($type);
|
$io->write($type);
|
||||||
}
|
}
|
||||||
ksort($packages[$type]);
|
ksort($packages[$type]);
|
||||||
|
|
||||||
|
@ -182,7 +223,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);
|
||||||
}
|
}
|
||||||
|
@ -197,6 +238,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);
|
||||||
|
@ -205,7 +251,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) {
|
||||||
|
@ -224,10 +270,10 @@ EOT
|
||||||
} else {
|
} else {
|
||||||
$output->write($indent . $package);
|
$output->write($indent . $package);
|
||||||
}
|
}
|
||||||
$output->writeln('');
|
$io->write('');
|
||||||
}
|
}
|
||||||
if ($tree) {
|
if ($showAllTypes) {
|
||||||
$output->writeln('');
|
$io->write('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,8 +286,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)
|
||||||
{
|
{
|
||||||
|
@ -274,9 +320,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);
|
||||||
|
@ -285,55 +331,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)
|
||||||
{
|
{
|
||||||
$output->writeln('<info>name</info> : ' . $package->getPrettyName());
|
$io = $this->getIO();
|
||||||
$output->writeln('<info>descrip.</info> : ' . $package->getDescription());
|
$io->write('<info>name</info> : ' . $package->getPrettyName());
|
||||||
$output->writeln('<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()));
|
||||||
$output->writeln('<info>type</info> : ' . $package->getType());
|
$this->printVersions($package, $versions, $installedRepo);
|
||||||
$output->writeln('<info>license</info> : ' . implode(', ', $package->getLicense()));
|
$io->write('<info>type</info> : ' . $package->getType());
|
||||||
$output->writeln('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
|
$this->printLicenses($package);
|
||||||
$output->writeln('<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()));
|
||||||
$output->writeln('<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;
|
||||||
|
|
||||||
$output->writeln(
|
$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()) {
|
||||||
$output->writeln("\n<info>support</info>");
|
$io->write("\n<info>support</info>");
|
||||||
foreach ($package->getSupport() as $type => $value) {
|
foreach ($package->getSupport() as $type => $value) {
|
||||||
$output->writeln('<comment>' . $type . '</comment> : '.$value);
|
$io->write('<comment>' . $type . '</comment> : '.$value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($package->getAutoload()) {
|
if ($package->getAutoload()) {
|
||||||
$output->writeln("\n<info>autoload</info>");
|
$io->write("\n<info>autoload</info>");
|
||||||
foreach ($package->getAutoload() as $type => $autoloads) {
|
foreach ($package->getAutoload() as $type => $autoloads) {
|
||||||
$output->writeln('<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) {
|
||||||
$output->writeln(($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) {
|
||||||
$output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
$io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
|
||||||
}
|
}
|
||||||
} elseif ($type === 'classmap') {
|
} elseif ($type === 'classmap') {
|
||||||
$output->writeln(implode(', ', $autoloads));
|
$io->write(implode(', ', $autoloads));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($package->getIncludePaths()) {
|
if ($package->getIncludePaths()) {
|
||||||
$output->writeln('<comment>include-path</comment>');
|
$io->write('<comment>include-path</comment>');
|
||||||
$output->writeln(implode(', ', $package->getIncludePaths()));
|
$io->write(implode(', ', $package->getIncludePaths()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +388,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));
|
||||||
|
@ -357,27 +404,172 @@ EOT
|
||||||
|
|
||||||
$versions = implode(', ', $versions);
|
$versions = implode(', ', $versions);
|
||||||
|
|
||||||
$output->writeln('<info>versions</info> : ' . $versions);
|
$this->getIO()->write('<info>versions</info> : ' . $versions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)}()) {
|
||||||
$output->writeln("\n<info>" . $title . "</info>");
|
$io->write("\n<info>" . $title . "</info>");
|
||||||
|
|
||||||
foreach ($links as $link) {
|
foreach ($links as $link) {
|
||||||
$output->writeln($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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init styles for tree
|
||||||
|
*
|
||||||
|
* @param OutputInterface $output
|
||||||
|
*/
|
||||||
|
protected function initStyles(OutputInterface $output)
|
||||||
|
{
|
||||||
|
$this->colors = array(
|
||||||
|
'green',
|
||||||
|
'yellow',
|
||||||
|
'cyan',
|
||||||
|
'magenta',
|
||||||
|
'blue',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($this->colors as $color) {
|
||||||
|
$style = new OutputFormatterStyle($color);
|
||||||
|
$output->getFormatter()->setStyle($color, $style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the tree
|
||||||
|
*
|
||||||
|
* @param PackageInterface|string $package
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param RepositoryInterface $distantRepos
|
||||||
|
* @param OutputInterface $output
|
||||||
|
*/
|
||||||
|
protected function displayPackageTree(PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $distantRepos, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$packagesInTree = array();
|
||||||
|
$packagesInTree[] = $package;
|
||||||
|
|
||||||
|
$output->write(sprintf('<info>%s</info>', $package->getPrettyName()));
|
||||||
|
$output->write(' ' . $package->getPrettyVersion());
|
||||||
|
$output->write(' ' . strtok($package->getDescription(), "\r\n"));
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
if (is_object($package)) {
|
||||||
|
$requires = $package->getRequires();
|
||||||
|
$treeBar = '├';
|
||||||
|
$j = 0;
|
||||||
|
$total = count($requires);
|
||||||
|
foreach ($requires as $requireName => $require) {
|
||||||
|
$j++;
|
||||||
|
if ($j == 0) {
|
||||||
|
$this->writeTreeLine($treeBar);
|
||||||
|
}
|
||||||
|
if ($j == $total) {
|
||||||
|
$treeBar = '└';
|
||||||
|
}
|
||||||
|
$level = 1;
|
||||||
|
$color = $this->colors[$level];
|
||||||
|
$info = sprintf('%s──<%s>%s</%s> %s', $treeBar, $color, $requireName, $color, $require->getPrettyConstraint());
|
||||||
|
$this->writeTreeLine($info);
|
||||||
|
|
||||||
|
$treeBar = str_replace('└', ' ', $treeBar);
|
||||||
|
|
||||||
|
$packagesInTree[] = $requireName;
|
||||||
|
|
||||||
|
$this->displayTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree, $output, $treeBar, $level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a package tree
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param PackageInterface|string $package
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param RepositoryInterface $distantRepos
|
||||||
|
* @param array $packagesInTree
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param string $previousTreeBar
|
||||||
|
* @param integer $level
|
||||||
|
*/
|
||||||
|
protected function displayTree($name, $package, RepositoryInterface $installedRepo, RepositoryInterface $distantRepos, array $packagesInTree, OutputInterface $output, $previousTreeBar = '├', $level = 1)
|
||||||
|
{
|
||||||
|
$previousTreeBar = str_replace('├', '│', $previousTreeBar);
|
||||||
|
list($package, $versions) = $this->getPackage($installedRepo, $distantRepos, $name, $package->getPrettyConstraint() === 'self.version' ? $package->getConstraint() : $package->getPrettyConstraint());
|
||||||
|
if (is_object($package)) {
|
||||||
|
$requires = $package->getRequires();
|
||||||
|
$treeBar = $previousTreeBar . ' ├';
|
||||||
|
$i = 0;
|
||||||
|
$total = count($requires);
|
||||||
|
foreach ($requires as $requireName => $require) {
|
||||||
|
$i++;
|
||||||
|
if ($i == $total) {
|
||||||
|
$treeBar = $previousTreeBar . ' └';
|
||||||
|
}
|
||||||
|
$colorIdent = $level % count($this->colors);
|
||||||
|
$color = $this->colors[$colorIdent];
|
||||||
|
$info = sprintf('%s──<%s>%s</%s> %s', $treeBar, $color, $requireName, $color, $require->getPrettyConstraint());
|
||||||
|
$this->writeTreeLine($info);
|
||||||
|
|
||||||
|
$treeBar = str_replace('└', ' ', $treeBar);
|
||||||
|
if (!in_array($requireName, $packagesInTree)) {
|
||||||
|
$packagesInTree[] = $requireName;
|
||||||
|
$this->displayTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree, $output, $treeBar, $level + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function writeTreeLine($line)
|
||||||
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
|
if (!$io->isDecorated()) {
|
||||||
|
$line = str_replace(array('└', '├', '──', '│'), array('`-', '|-', '-', '|'), $line);
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->write($line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -57,9 +57,10 @@ EOT
|
||||||
$im = $composer->getInstallationManager();
|
$im = $composer->getInstallationManager();
|
||||||
|
|
||||||
// Dispatch pre-status-command
|
// Dispatch pre-status-command
|
||||||
$composer->getEventDispatcher()->dispatchCommandEvent(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) {
|
||||||
$output->writeln('<info>No local changes</info>');
|
$io->writeError('<info>No local changes</info>');
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('<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,19 +91,19 @@ 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)));
|
||||||
$output->writeln('<info>'.$path.'</info>:');
|
$io->write('<info>'.$path.'</info>:');
|
||||||
$output->writeln($indentedChanges);
|
$io->write($indentedChanges);
|
||||||
} else {
|
} else {
|
||||||
$output->writeln($path);
|
$io->write($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($errors && !$input->getOption('verbose')) {
|
if ($errors && !$input->getOption('verbose')) {
|
||||||
$output->writeln('Use --verbose (-v) to see modified files');
|
$io->writeError('Use --verbose (-v) to see modified files');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch post-status-command
|
// Dispatch post-status-command
|
||||||
$composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_STATUS_CMD, true);
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true);
|
||||||
|
|
||||||
return $errors ? 1 : 0;
|
return $errors ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,13 +12,18 @@
|
||||||
|
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
use Composer\Installer;
|
use Composer\Installer;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
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\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
@ -49,9 +54,11 @@ class UpdateCommand extends Command
|
||||||
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('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
new InputOption('disable-tls', null, InputOption::VALUE_NONE, 'Disable SSL/TLS protection for HTTPS requests'),
|
||||||
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
new InputOption('cafile', null, InputOption::VALUE_REQUIRED, 'The path to a valid CA certificate file for SSL/TLS certificate verification'),
|
||||||
|
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.'),
|
||||||
|
new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
The <info>update</info> command reads the composer.json file from the
|
The <info>update</info> command reads the composer.json file from the
|
||||||
|
@ -69,6 +76,9 @@ You may also use an asterisk (*) pattern to limit the update operation to packag
|
||||||
from a specific vendor:
|
from a specific vendor:
|
||||||
|
|
||||||
<info>php composer.phar update vendor/package1 foo/* [...]</info>
|
<info>php composer.phar update vendor/package1 foo/* [...]</info>
|
||||||
|
|
||||||
|
To select packages names interactively with auto-completion use <info>-i</info>.
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
|
@ -76,14 +86,25 @@ 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')) {
|
||||||
$output->writeln('<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')) {
|
||||||
|
$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'));
|
||||||
|
|
||||||
|
$packages = $input->getArgument('packages');
|
||||||
|
|
||||||
|
if ($input->getOption('interactive')) {
|
||||||
|
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
||||||
|
}
|
||||||
|
|
||||||
$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);
|
||||||
|
@ -112,7 +133,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'))
|
||||||
|
@ -123,8 +145,9 @@ 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') : $packages)
|
||||||
->setWhitelistDependencies($input->getOption('with-dependencies'))
|
->setWhitelistDependencies($input->getOption('with-dependencies'))
|
||||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
||||||
->setPreferStable($input->getOption('prefer-stable'))
|
->setPreferStable($input->getOption('prefer-stable'))
|
||||||
|
@ -137,4 +160,66 @@ EOT
|
||||||
|
|
||||||
return $install->run();
|
return $install->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getPackagesInteractively(IOInterface $io, InputInterface $input, OutputInterface $output, Composer $composer, array $packages)
|
||||||
|
{
|
||||||
|
if (!$input->isInteractive()) {
|
||||||
|
throw new \InvalidArgumentException('--interactive cannot be used in non-interactive terminals.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$requires = array_merge(
|
||||||
|
$composer->getPackage()->getRequires(),
|
||||||
|
$composer->getPackage()->getDevRequires()
|
||||||
|
);
|
||||||
|
$autocompleterValues = array();
|
||||||
|
foreach ($requires as $require) {
|
||||||
|
$autocompleterValues[strtolower($require->getTarget())] = $require->getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
$installedPackages = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
|
||||||
|
foreach ($installedPackages as $package) {
|
||||||
|
$autocompleterValues[$package->getName()] = $package->getPrettyName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$helper = $this->getHelper('question');
|
||||||
|
$question = new Question('<comment>Enter package name: </comment>', null);
|
||||||
|
|
||||||
|
$io->writeError('<info>Press enter without value to end submission</info>');
|
||||||
|
|
||||||
|
do {
|
||||||
|
$autocompleterValues = array_diff($autocompleterValues, $packages);
|
||||||
|
$question->setAutocompleterValues($autocompleterValues);
|
||||||
|
$addedPackage = $helper->ask($input, $output, $question);
|
||||||
|
|
||||||
|
if (!is_string($addedPackage) || empty($addedPackage)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$addedPackage = strtolower($addedPackage);
|
||||||
|
if (!in_array($addedPackage, $packages)) {
|
||||||
|
$packages[] = $addedPackage;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
$packages = array_filter($packages);
|
||||||
|
if (!$packages) {
|
||||||
|
throw new \InvalidArgumentException('You must enter minimum one package.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = new Table($output);
|
||||||
|
$table->setHeaders(array('Selected packages'));
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
$table->addRow(array($package));
|
||||||
|
}
|
||||||
|
$table->render();
|
||||||
|
|
||||||
|
if ($io->askConfirmation(sprintf(
|
||||||
|
'Would you like to continue and update the above package%s [<comment>yes</comment>]? ',
|
||||||
|
1 === count($packages) ? '' : 's'
|
||||||
|
), true)) {
|
||||||
|
return $packages;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('Installation aborted.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,13 +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 InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
|
new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'),
|
||||||
|
new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
|
||||||
|
new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'),
|
||||||
|
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'),
|
||||||
|
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './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
|
||||||
);
|
);
|
||||||
|
@ -55,47 +65,97 @@ 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)) {
|
||||||
$output->writeln('<error>' . $file . ' not found.</error>');
|
$io->writeError('<error>' . $file . ' not found.</error>');
|
||||||
|
|
||||||
return 1;
|
return 3;
|
||||||
}
|
}
|
||||||
if (!is_readable($file)) {
|
if (!is_readable($file)) {
|
||||||
$output->writeln('<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');
|
||||||
|
$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, it is recommended that you run `composer update`.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$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) {
|
||||||
$output->writeln('<info>' . $file . ' is valid</info>');
|
$io->write('<info>' . $name . ' is valid</info>');
|
||||||
} elseif (!$errors && !$publishErrors) {
|
} elseif (!$errors && !$publishErrors) {
|
||||||
$output->writeln('<info>' . $file . ' is valid, but with a few warnings</info>');
|
$io->writeError('<info>' . $name . ' is valid, but with a few warnings</info>');
|
||||||
$output->writeln('<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) {
|
||||||
$output->writeln('<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>');
|
||||||
$output->writeln('<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>');
|
||||||
$output->writeln('<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 {
|
||||||
$output->writeln('<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(
|
||||||
'error' => array_merge($errors, $publishErrors),
|
'error' => $errors,
|
||||||
'warning' => $warnings,
|
'warning' => $warnings,
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($messages as $style => $msgs) {
|
foreach ($messages as $style => $msgs) {
|
||||||
foreach ($msgs as $msg) {
|
foreach ($msgs as $msg) {
|
||||||
$output->writeln('<' . $style . '>' . $msg . '</' . $style . '>');
|
$io->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors || $publishErrors ? 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) {
|
||||||
|
@ -91,21 +97,30 @@ class Compiler
|
||||||
$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()
|
||||||
->ignoreVCS(true)
|
->ignoreVCS(true)
|
||||||
->name('*.php')
|
->name('*.php')
|
||||||
|
->name('LICENSE')
|
||||||
->exclude('Tests')
|
->exclude('Tests')
|
||||||
|
->exclude('tests')
|
||||||
|
->exclude('docs')
|
||||||
->in(__DIR__.'/../../vendor/symfony/')
|
->in(__DIR__.'/../../vendor/symfony/')
|
||||||
->in(__DIR__.'/../../vendor/seld/jsonlint/src/')
|
->in(__DIR__.'/../../vendor/seld/jsonlint/')
|
||||||
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/src/')
|
->in(__DIR__.'/../../vendor/seld/cli-prompt/')
|
||||||
|
->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) {
|
||||||
|
@ -116,6 +131,7 @@ class Compiler
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_psr4.php'));
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_psr4.php'));
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
|
||||||
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php'));
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
|
||||||
if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) {
|
if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) {
|
||||||
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php'));
|
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php'));
|
||||||
|
@ -137,6 +153,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 +174,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 +250,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,8 +36,10 @@ 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,
|
||||||
|
'sort-packages' => false,
|
||||||
'optimize-autoloader' => false,
|
'optimize-autoloader' => false,
|
||||||
'classmap-authoritative' => false,
|
'classmap-authoritative' => false,
|
||||||
'prepend-autoloader' => true,
|
'prepend-autoloader' => true,
|
||||||
|
@ -45,9 +47,14 @@ class Config
|
||||||
'disable-tls' => false,
|
'disable-tls' => false,
|
||||||
'cafile' => null,
|
'cafile' => null,
|
||||||
'github-expose-hostname' => true,
|
'github-expose-hostname' => true,
|
||||||
|
'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
|
||||||
|
// gitlab-oauth
|
||||||
// http-basic
|
// http-basic
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -55,8 +62,8 @@ class Config
|
||||||
'packagist' => array(
|
'packagist' => array(
|
||||||
'type' => 'composer',
|
'type' => 'composer',
|
||||||
'url' => 'https?://packagist.org',
|
'url' => 'https?://packagist.org',
|
||||||
'allow_ssl_downgrade' => true, // TODO: check
|
'allow_ssl_downgrade' => true,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
private $config;
|
private $config;
|
||||||
|
@ -67,7 +74,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)
|
||||||
{
|
{
|
||||||
|
@ -108,7 +115,7 @@ class Config
|
||||||
// override defaults with given config
|
// override defaults with given config
|
||||||
if (!empty($config['config']) && is_array($config['config'])) {
|
if (!empty($config['config']) && is_array($config['config'])) {
|
||||||
foreach ($config['config'] as $key => $val) {
|
foreach ($config['config'] as $key => $val) {
|
||||||
if (in_array($key, array('github-oauth', 'http-basic')) && isset($this->config[$key])) {
|
if (in_array($key, array('github-oauth', 'gitlab-oauth', 'http-basic')) && isset($this->config[$key])) {
|
||||||
$this->config[$key] = array_merge($this->config[$key], $val);
|
$this->config[$key] = array_merge($this->config[$key], $val);
|
||||||
} else {
|
} else {
|
||||||
$this->config[$key] = $val;
|
$this->config[$key] = $val;
|
||||||
|
@ -180,7 +187,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];
|
||||||
|
@ -218,6 +225,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)) {
|
||||||
|
@ -335,7 +353,7 @@ class Config
|
||||||
* 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|gitlab-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|gitlab-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]);
|
||||||
|
|
|
@ -15,11 +15,11 @@ namespace Composer\Console;
|
||||||
use Symfony\Component\Console\Application as BaseApplication;
|
use Symfony\Component\Console\Application as BaseApplication;
|
||||||
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\ConsoleOutputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
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;
|
||||||
|
@ -56,6 +56,8 @@ class Application extends BaseApplication
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
static $shutdownRegistered = false;
|
||||||
|
|
||||||
if (function_exists('ini_set') && extension_loaded('xdebug')) {
|
if (function_exists('ini_set') && extension_loaded('xdebug')) {
|
||||||
ini_set('xdebug.show_exception_trace', false);
|
ini_set('xdebug.show_exception_trace', false);
|
||||||
ini_set('xdebug.scream', false);
|
ini_set('xdebug.scream', false);
|
||||||
|
@ -65,7 +67,20 @@ class Application extends BaseApplication
|
||||||
date_default_timezone_set(@date_default_timezone_get());
|
date_default_timezone_set(@date_default_timezone_get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorHandler::register();
|
if (!$shutdownRegistered) {
|
||||||
|
$shutdownRegistered = true;
|
||||||
|
|
||||||
|
register_shutdown_function(function () {
|
||||||
|
$lastError = error_get_last();
|
||||||
|
|
||||||
|
if ($lastError && $lastError['message'] &&
|
||||||
|
(strpos($lastError['message'], 'Allowed memory') !== false /*Zend PHP out of memory error*/ ||
|
||||||
|
strpos($lastError['message'], 'exceeded memory') !== false /*HHVM out of memory errors*/)) {
|
||||||
|
echo "\n". 'Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct('Composer', Composer::VERSION);
|
parent::__construct('Composer', Composer::VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,12 +104,10 @@ 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', '<')) {
|
// determine command name to be executed
|
||||||
$output->writeln('<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')) {
|
|
||||||
$commandName = '';
|
$commandName = '';
|
||||||
if ($name = $this->getCommandName($input)) {
|
if ($name = $this->getCommandName($input)) {
|
||||||
try {
|
try {
|
||||||
|
@ -102,10 +115,18 @@ class Application extends BaseApplication
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
|
|
||||||
if (time() > COMPOSER_DEV_WARNING_TIME) {
|
if ($commandName !== 'global') {
|
||||||
$output->writeln(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']));
|
if (PHP_VERSION_ID < 50302) {
|
||||||
|
$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 (extension_loaded('xdebug') && !getenv('COMPOSER_DISABLE_XDEBUG_WARN')) {
|
||||||
|
$io->writeError('<warning>You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug</warning>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined('COMPOSER_DEV_WARNING_TIME') && $commandName !== 'self-update' && $commandName !== 'selfupdate' && time() > COMPOSER_DEV_WARNING_TIME) {
|
||||||
|
$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']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +138,8 @@ class Application extends BaseApplication
|
||||||
if ($newWorkDir = $this->getNewWorkingDir($input)) {
|
if ($newWorkDir = $this->getNewWorkingDir($input)) {
|
||||||
$oldWorkingDir = getcwd();
|
$oldWorkingDir = getcwd();
|
||||||
chdir($newWorkDir);
|
chdir($newWorkDir);
|
||||||
if ($output->getVerbosity() >= 4) {
|
if ($io->isDebug() >= 4) {
|
||||||
$output->writeln('Changed CWD to ' . getcwd());
|
$io->writeError('Changed CWD to ' . getcwd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +150,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)) {
|
||||||
$output->writeln('<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));
|
||||||
}
|
}
|
||||||
|
@ -150,7 +171,7 @@ class Application extends BaseApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($startTime)) {
|
if (isset($startTime)) {
|
||||||
$output->writeln('<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;
|
||||||
|
@ -158,14 +179,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;
|
||||||
|
@ -176,6 +197,8 @@ 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) {
|
||||||
|
@ -186,23 +209,27 @@ class Application extends BaseApplication
|
||||||
|| (($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)
|
||||||
) {
|
) {
|
||||||
$output->writeln('<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')) {
|
||||||
$output->writeln('<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>');
|
||||||
$output->writeln('<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')) {
|
||||||
$output->writeln('<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>');
|
||||||
$output->writeln('<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>');
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::renderException($exception, $output);
|
if ($output instanceof ConsoleOutputInterface) {
|
||||||
|
parent::renderException($exception, $output->getErrorOutput());
|
||||||
|
} else {
|
||||||
|
parent::renderException($exception, $output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -218,7 +245,7 @@ class Application extends BaseApplication
|
||||||
$this->composer = Factory::create($this->io, null, $disablePlugins);
|
$this->composer = Factory::create($this->io, null, $disablePlugins);
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
if ($required) {
|
if ($required) {
|
||||||
$this->io->write($e->getMessage());
|
$this->io->writeError($e->getMessage());
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
} catch (JsonValidationException $e) {
|
} catch (JsonValidationException $e) {
|
||||||
|
@ -268,6 +295,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();
|
||||||
|
@ -316,16 +344,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) {
|
||||||
|
@ -65,6 +64,11 @@ class Pool
|
||||||
}
|
}
|
||||||
$this->stabilityFlags = $stabilityFlags;
|
$this->stabilityFlags = $stabilityFlags;
|
||||||
$this->filterRequires = $filterRequires;
|
$this->filterRequires = $filterRequires;
|
||||||
|
foreach ($filterRequires as $name => $constraint) {
|
||||||
|
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
|
||||||
|
unset($this->filterRequires[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setWhitelist($whitelist)
|
public function setWhitelist($whitelist)
|
||||||
|
@ -156,17 +160,25 @@ class Pool
|
||||||
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])) {
|
||||||
|
@ -266,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);
|
||||||
|
@ -307,10 +314,10 @@ class Pool
|
||||||
*
|
*
|
||||||
* @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();
|
||||||
|
@ -324,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,12 +87,29 @@ class Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($job && $job['cmd'] === 'install' && empty($packages)) {
|
if ($job && $job['cmd'] === 'install' && empty($packages)) {
|
||||||
|
|
||||||
|
// handle php/hhvm
|
||||||
|
if ($job['packageName'] === 'php' || $job['packageName'] === 'php-64bit' || $job['packageName'] === 'hhvm') {
|
||||||
|
$available = $this->pool->whatProvides($job['packageName']);
|
||||||
|
$version = count($available) ? $available[0]->getPrettyVersion() : phpversion();
|
||||||
|
|
||||||
|
$msg = "\n - This package requires ".$job['packageName'].$this->constraintToText($job['constraint']).' but ';
|
||||||
|
|
||||||
|
if (defined('HHVM_VERSION')) {
|
||||||
|
return $msg . 'your HHVM version does not satisfy that requirement.';
|
||||||
|
} elseif ($job['packageName'] === 'hhvm') {
|
||||||
|
return $msg . 'you are running this with PHP and not HHVM.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $msg . 'your PHP version ('. $version .') does not satisfy that requirement.';
|
||||||
|
}
|
||||||
|
|
||||||
// handle php extensions
|
// handle php extensions
|
||||||
if (0 === stripos($job['packageName'], 'ext-')) {
|
if (0 === stripos($job['packageName'], 'ext-')) {
|
||||||
$ext = substr($job['packageName'], 4);
|
$ext = substr($job['packageName'], 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';
|
||||||
|
|
||||||
return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'.';
|
return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle linked libs
|
// handle linked libs
|
||||||
|
@ -205,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->type = -1;
|
$this->bitfield = (0 << self::BITFIELD_DISABLED) |
|
||||||
|
($reason << self::BITFIELD_REASON) |
|
||||||
$this->ruleHash = substr(md5(implode(',', $this->literals)), 0, 5);
|
(255 << self::BITFIELD_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,8 +200,17 @@ class Rule
|
||||||
} else {
|
} else {
|
||||||
$targetName = $this->reasonData->getTarget();
|
$targetName = $this->reasonData->getTarget();
|
||||||
|
|
||||||
|
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
|
||||||
|
// handle php/hhvm
|
||||||
|
if (defined('HHVM_VERSION')) {
|
||||||
|
$text .= ' -> your HHVM version does not satisfy that requirement.';
|
||||||
|
} elseif ($targetName === 'hhvm') {
|
||||||
|
$text .= ' -> you are running this with PHP and not HHVM.';
|
||||||
|
} else {
|
||||||
|
$text .= ' -> your PHP version ('. phpversion() .') or value of "config.platform.php" in composer.json does not satisfy that requirement.';
|
||||||
|
}
|
||||||
|
} elseif (0 === strpos($targetName, 'ext-')) {
|
||||||
// handle php extensions
|
// handle php extensions
|
||||||
if (0 === strpos($targetName, 'ext-')) {
|
|
||||||
$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,24 +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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
@ -49,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;
|
||||||
|
@ -129,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) {
|
||||||
|
@ -163,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();
|
||||||
|
@ -198,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)
|
||||||
|
@ -237,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)
|
||||||
{
|
{
|
||||||
|
@ -262,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
|
||||||
|
@ -276,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)
|
||||||
{
|
{
|
||||||
|
@ -313,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);
|
||||||
|
@ -325,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);
|
||||||
|
|
||||||
|
@ -340,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;
|
||||||
|
@ -446,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];
|
||||||
|
@ -470,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);
|
||||||
|
@ -527,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();
|
||||||
|
|
||||||
|
@ -539,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();
|
||||||
}
|
}
|
||||||
|
@ -556,7 +617,7 @@ 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
|
||||||
|
@ -566,7 +627,7 @@ class Solver
|
||||||
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;
|
||||||
|
@ -585,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;
|
||||||
|
|
|
@ -36,7 +36,7 @@ class SolverProblemsException extends \RuntimeException
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -35,7 +35,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
||||||
$fileName = parent::download($package, $path);
|
$fileName = parent::download($package, $path);
|
||||||
|
|
||||||
if ($this->io->isVerbose()) {
|
if ($this->io->isVerbose()) {
|
||||||
$this->io->write(' Extracting archive');
|
$this->io->writeError(' Extracting archive');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -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->write(' 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;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +92,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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)) {
|
||||||
|
// update Bitbucket archives to the proper reference
|
||||||
|
$url = 'https://bitbucket.org/' . $match[1] . '/'. $match[2] . '/get/' . $package->getDistReference() . '.' . $match[4];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extension_loaded('openssl') && (0 === strpos($url, 'https:') || 0 === strpos($url, 'http://github.com'))) {
|
|
||||||
throw new \RuntimeException('You must enable the openssl extension to download files via https');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::processUrl($package, $url);
|
return parent::processUrl($package, $url);
|
||||||
|
|
|
@ -104,9 +104,8 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -122,11 +121,10 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -192,7 +190,7 @@ class DownloadManager
|
||||||
|
|
||||||
foreach ($sources as $i => $source) {
|
foreach ($sources as $i => $source) {
|
||||||
if (isset($e)) {
|
if (isset($e)) {
|
||||||
$this->io->write(' <warning>Now trying to download from ' . $source . '</warning>');
|
$this->io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
|
||||||
}
|
}
|
||||||
$package->setInstallationSource($source);
|
$package->setInstallationSource($source);
|
||||||
try {
|
try {
|
||||||
|
@ -206,7 +204,7 @@ class DownloadManager
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write(
|
$this->io->writeError(
|
||||||
' <warning>Failed to download '.
|
' <warning>Failed to download '.
|
||||||
$package->getPrettyName().
|
$package->getPrettyName().
|
||||||
' from ' . $source . ': '.
|
' from ' . $source . ': '.
|
||||||
|
|
|
@ -17,7 +17,6 @@ use Composer\Cache;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
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;
|
||||||
|
@ -82,7 +81,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->write(" - 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)) {
|
||||||
|
@ -90,11 +89,11 @@ class FileDownloader implements DownloaderInterface
|
||||||
return $this->doDownload($package, $path, $url);
|
return $this->doDownload($package, $path, $url);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
|
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
|
||||||
} elseif (count($urls)) {
|
} elseif (count($urls)) {
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
$this->io->write(' Failed, trying the next URL');
|
$this->io->writeError(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!count($urls)) {
|
if (!count($urls)) {
|
||||||
|
@ -103,7 +102,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function doDownload(PackageInterface $package, $path, $url)
|
protected function doDownload(PackageInterface $package, $path, $url)
|
||||||
|
@ -128,7 +127,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
// download if we don't have it in cache or the cache is invalidated
|
// download if we don't have it in cache or the cache is invalidated
|
||||||
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
|
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
|
||||||
if (!$this->outputProgress) {
|
if (!$this->outputProgress) {
|
||||||
$this->io->write(' Downloading');
|
$this->io->writeError(' Downloading');
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to download 3 times then fail hard
|
// try to download 3 times then fail hard
|
||||||
|
@ -143,7 +142,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
if ($this->io->isVerbose()) {
|
if ($this->io->isVerbose()) {
|
||||||
$this->io->write(' Download failed, retrying...');
|
$this->io->writeError(' Download failed, retrying...');
|
||||||
}
|
}
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +152,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
$this->cache->copyFrom($cacheKey, $fileName);
|
$this->cache->copyFrom($cacheKey, $fileName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->io->write(' Loading from cache');
|
$this->io->writeError(' Loading from cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists($fileName)) {
|
if (!file_exists($fileName)) {
|
||||||
|
@ -206,7 +205,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
*/
|
*/
|
||||||
public function remove(PackageInterface $package, $path)
|
public function remove(PackageInterface $package, $path)
|
||||||
{
|
{
|
||||||
$this->io->write(" - 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.');
|
||||||
}
|
}
|
||||||
|
@ -229,9 +228,8 @@ class FileDownloader implements DownloaderInterface
|
||||||
*
|
*
|
||||||
* @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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Composer\Downloader;
|
||||||
*/
|
*/
|
||||||
class FilesystemException extends \Exception
|
class FilesystemException extends \Exception
|
||||||
{
|
{
|
||||||
public function __construct($message = null, $code = null, \Exception $previous = null)
|
public function __construct($message = '', $code = 0, \Exception $previous = null)
|
||||||
{
|
{
|
||||||
parent::__construct("Filesystem exception: \n".$message, $code, $previous);
|
parent::__construct("Filesystem exception: \n".$message, $code, $previous);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -26,6 +25,7 @@ use Composer\Config;
|
||||||
class GitDownloader extends VcsDownloader
|
class GitDownloader extends VcsDownloader
|
||||||
{
|
{
|
||||||
private $hasStashedChanges = false;
|
private $hasStashedChanges = false;
|
||||||
|
private $hasDiscardedChanges = false;
|
||||||
private $gitUtil;
|
private $gitUtil;
|
||||||
|
|
||||||
public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
|
public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
|
||||||
|
@ -45,7 +45,7 @@ class GitDownloader extends VcsDownloader
|
||||||
$ref = $package->getSourceReference();
|
$ref = $package->getSourceReference();
|
||||||
$flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : '';
|
$flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : '';
|
||||||
$command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer';
|
$command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer';
|
||||||
$this->io->write(" Cloning ".$ref);
|
$this->io->writeError(" Cloning ".$ref);
|
||||||
|
|
||||||
$commandCallable = function ($url) use ($ref, $path, $command) {
|
$commandCallable = function ($url) use ($ref, $path, $command) {
|
||||||
return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref));
|
return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref));
|
||||||
|
@ -74,11 +74,11 @@ 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();
|
||||||
$this->io->write(" Checking out ".$ref);
|
$this->io->writeError(" Checking out ".$ref);
|
||||||
$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) {
|
||||||
|
@ -143,14 +143,14 @@ class GitDownloader extends VcsDownloader
|
||||||
$changes = array_map(function ($elem) {
|
$changes = array_map(function ($elem) {
|
||||||
return ' '.$elem;
|
return ' '.$elem;
|
||||||
}, preg_split('{\s*\r?\n\s*}', $changes));
|
}, preg_split('{\s*\r?\n\s*}', $changes));
|
||||||
$this->io->write(' <error>The package has modified files:</error>');
|
$this->io->writeError(' <error>The package has modified files:</error>');
|
||||||
$this->io->write(array_slice($changes, 0, 10));
|
$this->io->writeError(array_slice($changes, 0, 10));
|
||||||
if (count($changes) > 10) {
|
if (count($changes) > 10) {
|
||||||
$this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
|
$this->io->writeError(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -167,21 +167,26 @@ class GitDownloader extends VcsDownloader
|
||||||
throw new \RuntimeException('Update aborted');
|
throw new \RuntimeException('Update aborted');
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
$this->io->write($changes);
|
$this->io->writeError($changes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
$this->viewDiff($path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
help:
|
help:
|
||||||
$this->io->write(array(
|
$this->io->writeError(array(
|
||||||
' 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->write(' 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');
|
||||||
}
|
}
|
||||||
$this->io->write(' ? - print help');
|
$this->io->writeError(' ? - print help');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,11 +200,13 @@ class GitDownloader extends VcsDownloader
|
||||||
$path = $this->normalizePath($path);
|
$path = $this->normalizePath($path);
|
||||||
if ($this->hasStashedChanges) {
|
if ($this->hasStashedChanges) {
|
||||||
$this->hasStashedChanges = false;
|
$this->hasStashedChanges = false;
|
||||||
$this->io->write(' <info>Re-applying stashed changes</info>');
|
$this->io->writeError(' <info>Re-applying stashed changes</info>');
|
||||||
if (0 !== $this->process->execute('git stash pop', $output, $path)) {
|
if (0 !== $this->process->execute('git stash pop', $output, $path)) {
|
||||||
throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput());
|
throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->hasDiscardedChanges = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,13 +216,19 @@ class GitDownloader extends VcsDownloader
|
||||||
* @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';
|
$force = $this->hasDiscardedChanges || $this->hasStashedChanges ? '-f ' : '';
|
||||||
|
|
||||||
|
// 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 '.$force.'%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 +242,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 '.$force.'-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 +255,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 '.$force.'-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;
|
||||||
}
|
}
|
||||||
|
@ -261,7 +274,7 @@ class GitDownloader extends VcsDownloader
|
||||||
|
|
||||||
// reference was not found (prints "fatal: reference is not a tree: $ref")
|
// reference was not found (prints "fatal: reference is not a tree: $ref")
|
||||||
if (false !== strpos($this->process->getErrorOutput(), $reference)) {
|
if (false !== strpos($this->process->getErrorOutput(), $reference)) {
|
||||||
$this->io->write(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
|
$this->io->writeError(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput());
|
throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput());
|
||||||
|
@ -306,6 +319,8 @@ class GitDownloader extends VcsDownloader
|
||||||
if (0 !== $this->process->execute('git reset --hard', $output, $path)) {
|
if (0 !== $this->process->execute('git reset --hard', $output, $path)) {
|
||||||
throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput());
|
throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->hasDiscardedChanges = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,13 +330,27 @@ class GitDownloader extends VcsDownloader
|
||||||
protected function stashChanges($path)
|
protected function stashChanges($path)
|
||||||
{
|
{
|
||||||
$path = $this->normalizePath($path);
|
$path = $this->normalizePath($path);
|
||||||
if (0 !== $this->process->execute('git stash', $output, $path)) {
|
if (0 !== $this->process->execute('git stash --include-untracked', $output, $path)) {
|
||||||
throw new \RuntimeException("Could not stash changes\n\n:".$this->process->getErrorOutput());
|
throw new \RuntimeException("Could not stash changes\n\n:".$this->process->getErrorOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
$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) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ class HgDownloader extends VcsDownloader
|
||||||
{
|
{
|
||||||
$url = ProcessExecutor::escape($url);
|
$url = ProcessExecutor::escape($url);
|
||||||
$ref = ProcessExecutor::escape($package->getSourceReference());
|
$ref = ProcessExecutor::escape($package->getSourceReference());
|
||||||
$this->io->write(" Cloning ".$package->getSourceReference());
|
$this->io->writeError(" Cloning ".$package->getSourceReference());
|
||||||
$command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
|
$command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
|
||||||
if (0 !== $this->process->execute($command, $ignoredOutput)) {
|
if (0 !== $this->process->execute($command, $ignoredOutput)) {
|
||||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||||
|
@ -45,10 +45,10 @@ class HgDownloader extends VcsDownloader
|
||||||
{
|
{
|
||||||
$url = ProcessExecutor::escape($url);
|
$url = ProcessExecutor::escape($url);
|
||||||
$ref = ProcessExecutor::escape($target->getSourceReference());
|
$ref = ProcessExecutor::escape($target->getSourceReference());
|
||||||
$this->io->write(" 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.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class PerforceDownloader extends VcsDownloader
|
||||||
$ref = $package->getSourceReference();
|
$ref = $package->getSourceReference();
|
||||||
$label = $this->getLabelFromSourceReference($ref);
|
$label = $this->getLabelFromSourceReference($ref);
|
||||||
|
|
||||||
$this->io->write(' Cloning ' . $ref);
|
$this->io->writeError(' Cloning ' . $ref);
|
||||||
$this->initPerforce($package, $path, $url);
|
$this->initPerforce($package, $path, $url);
|
||||||
$this->perforce->setStream($ref);
|
$this->perforce->setStream($ref);
|
||||||
$this->perforce->p4Login($this->io);
|
$this->perforce->p4Login($this->io);
|
||||||
|
@ -85,7 +85,7 @@ class PerforceDownloader extends VcsDownloader
|
||||||
*/
|
*/
|
||||||
public function getLocalChanges(PackageInterface $package, $path)
|
public function getLocalChanges(PackageInterface $package, $path)
|
||||||
{
|
{
|
||||||
$this->io->write('Perforce driver does not check for local changes before overriding', true);
|
$this->io->writeError('Perforce driver does not check for local changes before overriding', true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Downloader;
|
||||||
|
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Util\Svn as SvnUtil;
|
use Composer\Util\Svn as SvnUtil;
|
||||||
|
use Composer\Repository\VcsRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ben Bieker <mail@ben-bieker.de>
|
* @author Ben Bieker <mail@ben-bieker.de>
|
||||||
|
@ -21,6 +22,8 @@ use Composer\Util\Svn as SvnUtil;
|
||||||
*/
|
*/
|
||||||
class SvnDownloader extends VcsDownloader
|
class SvnDownloader extends VcsDownloader
|
||||||
{
|
{
|
||||||
|
protected $cacheCredentials = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +32,15 @@ class SvnDownloader extends VcsDownloader
|
||||||
SvnUtil::cleanEnv();
|
SvnUtil::cleanEnv();
|
||||||
$ref = $package->getSourceReference();
|
$ref = $package->getSourceReference();
|
||||||
|
|
||||||
$this->io->write(" Checking out ".$package->getSourceReference());
|
$repo = $package->getRepository();
|
||||||
|
if ($repo instanceof VcsRepository) {
|
||||||
|
$repoConfig = $repo->getRepoConfig();
|
||||||
|
if (array_key_exists('svn-cache-credentials', $repoConfig)) {
|
||||||
|
$this->cacheCredentials = (bool) $repoConfig['svn-cache-credentials'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->writeError(" Checking out ".$package->getSourceReference());
|
||||||
$this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path);
|
$this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +53,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 = "";
|
||||||
|
@ -52,7 +63,7 @@ class SvnDownloader extends VcsDownloader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write(" Checking out " . $ref);
|
$this->io->writeError(" Checking out " . $ref);
|
||||||
$this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path);
|
$this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +96,7 @@ class SvnDownloader extends VcsDownloader
|
||||||
protected function execute($baseUrl, $command, $url, $cwd = null, $path = null)
|
protected function execute($baseUrl, $command, $url, $cwd = null, $path = null)
|
||||||
{
|
{
|
||||||
$util = new SvnUtil($baseUrl, $this->io, $this->config);
|
$util = new SvnUtil($baseUrl, $this->io, $this->config);
|
||||||
|
$util->setCacheCredentials($this->cacheCredentials);
|
||||||
try {
|
try {
|
||||||
return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose());
|
return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose());
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
|
@ -114,10 +126,10 @@ class SvnDownloader extends VcsDownloader
|
||||||
$changes = array_map(function ($elem) {
|
$changes = array_map(function ($elem) {
|
||||||
return ' '.$elem;
|
return ' '.$elem;
|
||||||
}, preg_split('{\s*\r?\n\s*}', $changes));
|
}, preg_split('{\s*\r?\n\s*}', $changes));
|
||||||
$this->io->write(' <error>The package has modified files:</error>');
|
$this->io->writeError(' <error>The package has modified files:</error>');
|
||||||
$this->io->write(array_slice($changes, 0, 10));
|
$this->io->writeError(array_slice($changes, 0, 10));
|
||||||
if (count($changes) > 10) {
|
if (count($changes) > 10) {
|
||||||
$this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
|
$this->io->writeError(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -130,12 +142,12 @@ class SvnDownloader extends VcsDownloader
|
||||||
throw new \RuntimeException('Update aborted');
|
throw new \RuntimeException('Update aborted');
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
$this->io->write($changes);
|
$this->io->writeError($changes);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
$this->io->write(array(
|
$this->io->writeError(array(
|
||||||
' 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',
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace Composer\Downloader;
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
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;
|
||||||
|
@ -25,9 +24,13 @@ use Composer\Util\Filesystem;
|
||||||
*/
|
*/
|
||||||
abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterface
|
abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterface
|
||||||
{
|
{
|
||||||
|
/** @var IOInterface */
|
||||||
protected $io;
|
protected $io;
|
||||||
|
/** @var Config */
|
||||||
protected $config;
|
protected $config;
|
||||||
|
/** @var ProcessExecutor */
|
||||||
protected $process;
|
protected $process;
|
||||||
|
/** @var Filesystem */
|
||||||
protected $filesystem;
|
protected $filesystem;
|
||||||
|
|
||||||
public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
|
public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
|
||||||
|
@ -55,7 +58,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->write(" - 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();
|
||||||
|
@ -68,9 +71,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
break;
|
break;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
|
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage());
|
||||||
} elseif (count($urls)) {
|
} elseif (count($urls)) {
|
||||||
$this->io->write(' Failed, trying the next URL');
|
$this->io->writeError(' Failed, trying the next URL');
|
||||||
}
|
}
|
||||||
if (!count($urls)) {
|
if (!count($urls)) {
|
||||||
throw $e;
|
throw $e;
|
||||||
|
@ -78,7 +81,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,11 +104,11 @@ 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->write(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
|
$this->io->writeError(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
|
||||||
|
|
||||||
$this->cleanChanges($initial, $path, true);
|
$this->cleanChanges($initial, $path, true);
|
||||||
$urls = $target->getSourceUrls();
|
$urls = $target->getSourceUrls();
|
||||||
|
@ -118,9 +121,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
break;
|
break;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($this->io->isDebug()) {
|
if ($this->io->isDebug()) {
|
||||||
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
|
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage());
|
||||||
} elseif (count($urls)) {
|
} elseif (count($urls)) {
|
||||||
$this->io->write(' Failed, trying the next URL');
|
$this->io->writeError(' Failed, trying the next URL');
|
||||||
} else {
|
} else {
|
||||||
// in case of failed update, try to reapply the changes before aborting
|
// in case of failed update, try to reapply the changes before aborting
|
||||||
$this->reapplyChanges($path);
|
$this->reapplyChanges($path);
|
||||||
|
@ -147,12 +150,15 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
return ' ' . $line;
|
return ' ' . $line;
|
||||||
}, explode("\n", $logs)));
|
}, explode("\n", $logs)));
|
||||||
|
|
||||||
$this->io->write(' '.$message);
|
// escape angle brackets for proper output in the console
|
||||||
$this->io->write($logs);
|
$logs = str_replace('<', '\<', $logs);
|
||||||
|
|
||||||
|
$this->io->writeError(' '.$message);
|
||||||
|
$this->io->writeError($logs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->write('');
|
$this->io->writeError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +166,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
||||||
*/
|
*/
|
||||||
public function remove(PackageInterface $package, $path)
|
public function remove(PackageInterface $package, $path)
|
||||||
{
|
{
|
||||||
$this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
|
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
|
||||||
$this->cleanChanges($package, $path, false);
|
$this->cleanChanges($package, $path, false);
|
||||||
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.');
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?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\Config;
|
||||||
|
use Composer\Cache;
|
||||||
|
use Composer\EventDispatcher\EventDispatcher;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Xz archive downloader.
|
||||||
|
*
|
||||||
|
* @author Pavel Puchkin <i@neoascetic.me>
|
||||||
|
* @author Pierre Rudloff <contact@rudloff.pro>
|
||||||
|
*/
|
||||||
|
class XzDownloader extends ArchiveDownloader
|
||||||
|
{
|
||||||
|
protected $process;
|
||||||
|
|
||||||
|
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
|
||||||
|
{
|
||||||
|
$this->process = $process ?: new ProcessExecutor($io);
|
||||||
|
|
||||||
|
parent::__construct($io, $config, $eventDispatcher, $cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extract($file, $path)
|
||||||
|
{
|
||||||
|
$command = 'tar -xJf ' . ProcessExecutor::escape($file) . ' -C ' . ProcessExecutor::escape($path);
|
||||||
|
|
||||||
|
if (0 === $this->process->execute($command, $ignoredOutput)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
|
||||||
|
|
||||||
|
throw new \RuntimeException($processError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getFileName(PackageInterface $package, $path)
|
||||||
|
{
|
||||||
|
return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,6 +45,7 @@ class EventDispatcher
|
||||||
protected $loader;
|
protected $loader;
|
||||||
protected $process;
|
protected $process;
|
||||||
protected $listeners;
|
protected $listeners;
|
||||||
|
private $eventStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -58,6 +59,7 @@ class EventDispatcher
|
||||||
$this->composer = $composer;
|
$this->composer = $composer;
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->process = $process ?: new ProcessExecutor($io);
|
$this->process = $process ?: new ProcessExecutor($io);
|
||||||
|
$this->eventStack = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,36 +97,28 @@ class EventDispatcher
|
||||||
/**
|
/**
|
||||||
* Dispatch a package event.
|
* Dispatch a package event.
|
||||||
*
|
*
|
||||||
* @param string $eventName The constant in ScriptEvents
|
* @param string $eventName The constant in PackageEvents
|
||||||
* @param boolean $devMode Whether or not we are in dev mode
|
* @param bool $devMode Whether or not we are in dev mode
|
||||||
|
* @param PolicyInterface $policy The policy
|
||||||
|
* @param Pool $pool The pool
|
||||||
|
* @param CompositeRepository $installedRepo The installed repository
|
||||||
|
* @param Request $request The request
|
||||||
|
* @param array $operations The list of operations
|
||||||
* @param OperationInterface $operation The package being installed/updated/removed
|
* @param OperationInterface $operation The package being installed/updated/removed
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation)
|
|
||||||
{
|
|
||||||
return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch a command event.
|
|
||||||
*
|
*
|
||||||
* @param string $eventName The constant in ScriptEvents
|
|
||||||
* @param boolean $devMode Whether or not we are in dev mode
|
|
||||||
* @param array $additionalArgs Arguments passed by the user
|
|
||||||
* @param array $flags Optional flags to pass data not as argument
|
|
||||||
* @return int return code of the executed script if any, for php scripts a false return
|
* @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
|
* value is changed to 1, anything else to 0
|
||||||
*/
|
*/
|
||||||
public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array(), $flags = array())
|
public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
|
||||||
{
|
{
|
||||||
return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags));
|
return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations, $operation));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch a installer event.
|
* Dispatch a installer event.
|
||||||
*
|
*
|
||||||
* @param string $eventName The constant in InstallerEvents
|
* @param string $eventName The constant in InstallerEvents
|
||||||
|
* @param bool $devMode Whether or not we are in dev mode
|
||||||
* @param PolicyInterface $policy The policy
|
* @param PolicyInterface $policy The policy
|
||||||
* @param Pool $pool The pool
|
* @param Pool $pool The pool
|
||||||
* @param CompositeRepository $installedRepo The installed repository
|
* @param CompositeRepository $installedRepo The installed repository
|
||||||
|
@ -134,9 +128,9 @@ class EventDispatcher
|
||||||
* @return int return code of the executed script if any, for php scripts a false return
|
* @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
|
* value is changed to 1, anything else to 0
|
||||||
*/
|
*/
|
||||||
public function dispatchInstallerEvent($eventName, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
|
public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
|
||||||
{
|
{
|
||||||
return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $policy, $pool, $installedRepo, $request, $operations));
|
return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,30 +138,40 @@ 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)
|
||||||
{
|
{
|
||||||
$listeners = $this->getListeners($event);
|
$listeners = $this->getListeners($event);
|
||||||
|
|
||||||
|
$this->pushEvent($event);
|
||||||
|
|
||||||
$return = 0;
|
$return = 0;
|
||||||
foreach ($listeners as $callable) {
|
foreach ($listeners as $callable) {
|
||||||
if (!is_string($callable) && is_callable($callable)) {
|
if (!is_string($callable) && is_callable($callable)) {
|
||||||
$event = $this->checkListenerExpectedEvent($callable, $event);
|
$event = $this->checkListenerExpectedEvent($callable, $event);
|
||||||
$return = false === call_user_func($callable, $event) ? 1 : 0;
|
$return = false === call_user_func($callable, $event) ? 1 : 0;
|
||||||
|
} elseif ($this->isComposerScript($callable)) {
|
||||||
|
if ($this->io->isVerbose()) {
|
||||||
|
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable));
|
||||||
|
}
|
||||||
|
$scriptName = substr($callable, 1);
|
||||||
|
$args = $event->getArguments();
|
||||||
|
$flags = $event->getFlags();
|
||||||
|
$return = $this->dispatch($scriptName, new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags));
|
||||||
} elseif ($this->isPhpScript($callable)) {
|
} elseif ($this->isPhpScript($callable)) {
|
||||||
$className = substr($callable, 0, strpos($callable, '::'));
|
$className = substr($callable, 0, strpos($callable, '::'));
|
||||||
$methodName = substr($callable, strpos($callable, '::') + 2);
|
$methodName = substr($callable, strpos($callable, '::') + 2);
|
||||||
|
|
||||||
if (!class_exists($className)) {
|
if (!class_exists($className)) {
|
||||||
$this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
|
$this->io->writeError('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!is_callable($callable)) {
|
if (!is_callable($callable)) {
|
||||||
$this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
|
$this->io->writeError('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,13 +179,19 @@ class EventDispatcher
|
||||||
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
|
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$message = "Script %s handling the %s event terminated with an exception";
|
$message = "Script %s handling the %s event terminated with an exception";
|
||||||
$this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
|
$this->io->writeError('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
|
||||||
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);
|
||||||
$this->io->write(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
|
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()));
|
||||||
|
|
||||||
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
||||||
}
|
}
|
||||||
|
@ -192,6 +202,8 @@ class EventDispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->popEvent();
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +216,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,10 +232,6 @@ class EventDispatcher
|
||||||
*/
|
*/
|
||||||
protected function checkListenerExpectedEvent($target, Event $event)
|
protected function checkListenerExpectedEvent($target, Event $event)
|
||||||
{
|
{
|
||||||
if (!$event instanceof Script\Event) {
|
|
||||||
return $event;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reflected = new \ReflectionParameter($target, 0);
|
$reflected = new \ReflectionParameter($target, 0);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -232,8 +246,24 @@ class EventDispatcher
|
||||||
|
|
||||||
$expected = $typehint->getName();
|
$expected = $typehint->getName();
|
||||||
|
|
||||||
|
// BC support
|
||||||
if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') {
|
if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') {
|
||||||
$event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments());
|
$event = new \Composer\Script\CommandEvent(
|
||||||
|
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!$event instanceof $expected && $expected === 'Composer\Script\PackageEvent') {
|
||||||
|
$event = new \Composer\Script\PackageEvent(
|
||||||
|
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(),
|
||||||
|
$event->getPolicy(), $event->getPool(), $event->getInstalledRepo(), $event->getRequest(),
|
||||||
|
$event->getOperations(), $event->getOperation()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!$event instanceof $expected && $expected === 'Composer\Script\Event') {
|
||||||
|
$event = new \Composer\Script\Event(
|
||||||
|
$event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(),
|
||||||
|
$event->getArguments(), $event->getFlags()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $event;
|
return $event;
|
||||||
|
@ -244,7 +274,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)
|
||||||
{
|
{
|
||||||
|
@ -298,7 +328,7 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -340,10 +370,48 @@ 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)
|
||||||
{
|
{
|
||||||
return false === strpos($callable, ' ') && false !== strpos($callable, '::');
|
return false === strpos($callable, ' ') && false !== strpos($callable, '::');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if string given references a composer run-script
|
||||||
|
*
|
||||||
|
* @param string $callable
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isComposerScript($callable)
|
||||||
|
{
|
||||||
|
return '@' === substr($callable, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push an event to the stack of active event
|
||||||
|
*
|
||||||
|
* @param Event $event
|
||||||
|
* @throws \RuntimeException
|
||||||
|
* @return number
|
||||||
|
*/
|
||||||
|
protected function pushEvent(Event $event)
|
||||||
|
{
|
||||||
|
$eventName = $event->getName();
|
||||||
|
if (in_array($eventName, $this->eventStack)) {
|
||||||
|
throw new \RuntimeException(sprintf("Circular call to script handler '%s' detected", $eventName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_push($this->eventStack, $eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops the active event from the stack
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function popEvent()
|
||||||
|
{
|
||||||
|
return array_pop($this->eventStack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,16 @@ 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\RepositoryInterface;
|
|
||||||
use Composer\Repository\WritableRepositoryInterface;
|
use Composer\Repository\WritableRepositoryInterface;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\RemoteFilesystem;
|
use Composer\Util\RemoteFilesystem;
|
||||||
use Composer\Util\Filesystem;
|
|
||||||
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;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Seld\JsonLint\JsonParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a configured instance of composer.
|
* Creates a configured instance of composer.
|
||||||
|
@ -39,8 +38,8 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||||
class Factory
|
class Factory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return string
|
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected static function getHomeDir()
|
protected static function getHomeDir()
|
||||||
{
|
{
|
||||||
|
@ -64,7 +63,6 @@ class Factory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $home
|
* @param string $home
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected static function getCacheDir($home)
|
protected static function getCacheDir($home)
|
||||||
|
@ -116,10 +114,10 @@ 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->write('Loading config file ' . $file->getPath());
|
$io->writeError('Loading config file ' . $file->getPath());
|
||||||
}
|
}
|
||||||
$config->merge($file->read());
|
$config->merge($file->read());
|
||||||
}
|
}
|
||||||
|
@ -129,7 +127,7 @@ class Factory
|
||||||
$file = new JsonFile($config->get('home').'/auth.json');
|
$file = new JsonFile($config->get('home').'/auth.json');
|
||||||
if ($file->exists()) {
|
if ($file->exists()) {
|
||||||
if ($io && $io->isDebug()) {
|
if ($io && $io->isDebug()) {
|
||||||
$io->write('Loading config file ' . $file->getPath());
|
$io->writeError('Loading config file ' . $file->getPath());
|
||||||
}
|
}
|
||||||
$config->merge(array('config' => $file->read()));
|
$config->merge(array('config' => $file->read()));
|
||||||
}
|
}
|
||||||
|
@ -219,11 +217,19 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
$file->validateSchema(JsonFile::LAX_SCHEMA);
|
$file->validateSchema(JsonFile::LAX_SCHEMA);
|
||||||
|
$jsonParser = new JsonParser;
|
||||||
|
try {
|
||||||
|
$jsonParser->parse(file_get_contents($localConfig), JsonParser::DETECT_KEY_CONFLICTS);
|
||||||
|
} catch (\Seld\JsonLint\DuplicateKeyException $e) {
|
||||||
|
$details = $e->getDetails();
|
||||||
|
$io->writeError('<warning>Key '.$details['key'].' is a duplicate in '.$localConfig.' at line '.$details['line'].'</warning>');
|
||||||
|
}
|
||||||
|
|
||||||
$localConfig = $file->read();
|
$localConfig = $file->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +238,12 @@ class Factory
|
||||||
$config->merge($localConfig);
|
$config->merge($localConfig);
|
||||||
if (isset($composerFile)) {
|
if (isset($composerFile)) {
|
||||||
if ($io && $io->isDebug()) {
|
if ($io && $io->isDebug()) {
|
||||||
$io->write('Loading config file ' . $composerFile);
|
$io->writeError('Loading config file ' . $composerFile);
|
||||||
}
|
}
|
||||||
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
|
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
|
||||||
if ($localAuthFile->exists()) {
|
if ($localAuthFile->exists()) {
|
||||||
if ($io && $io->isDebug()) {
|
if ($io && $io->isDebug()) {
|
||||||
$io->write('Loading config file ' . $localAuthFile->getPath());
|
$io->writeError('Loading config file ' . $localAuthFile->getPath());
|
||||||
}
|
}
|
||||||
$config->merge(array('config' => $localAuthFile->read()));
|
$config->merge(array('config' => $localAuthFile->read()));
|
||||||
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
|
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
|
||||||
|
@ -254,9 +260,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
|
||||||
|
@ -270,10 +273,17 @@ class Factory
|
||||||
// load local repository
|
// load local repository
|
||||||
$this->addLocalRepository($rm, $vendorDir);
|
$this->addLocalRepository($rm, $vendorDir);
|
||||||
|
|
||||||
|
// force-set the version of the global package if not defined as
|
||||||
|
// guessing it adds no value and only takes time
|
||||||
|
if (!$fullLoad && !isset($localConfig['version'])) {
|
||||||
|
$localConfig['version'] = '1.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
$package = $loader->load($localConfig);
|
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser);
|
||||||
|
$package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd);
|
||||||
$composer->setPackage($package);
|
$composer->setPackage($package);
|
||||||
|
|
||||||
// initialize installation manager
|
// initialize installation manager
|
||||||
|
@ -295,12 +305,10 @@ class Factory
|
||||||
|
|
||||||
if ($fullLoad) {
|
if ($fullLoad) {
|
||||||
$globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins);
|
$globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins);
|
||||||
$pm = $this->createPluginManager($io, $composer, $globalComposer);
|
$pm = $this->createPluginManager($io, $composer, $globalComposer, $disablePlugins);
|
||||||
$composer->setPluginManager($pm);
|
$composer->setPluginManager($pm);
|
||||||
|
|
||||||
if (!$disablePlugins) {
|
|
||||||
$pm->loadInstalledPlugins();
|
$pm->loadInstalledPlugins();
|
||||||
}
|
|
||||||
|
|
||||||
// once we have plugins and custom installers we can
|
// once we have plugins and custom installers we can
|
||||||
// purge packages from local repos if they have been deleted on the filesystem
|
// purge packages from local repos if they have been deleted on the filesystem
|
||||||
|
@ -315,8 +323,7 @@ class Factory
|
||||||
? substr($composerFile, 0, -4).'lock'
|
? substr($composerFile, 0, -4).'lock'
|
||||||
: $composerFile . '.lock';
|
: $composerFile . '.lock';
|
||||||
|
|
||||||
$locker = new Package\Locker($io, new JsonFile($lockFile, $rfs), $rm, $im, md5_file($composerFile)); // can we reuse same object?
|
$locker = new Package\Locker($io, new JsonFile($lockFile, $rfs), $rm, $im, file_get_contents($composerFile));
|
||||||
|
|
||||||
$composer->setLocker($locker);
|
$composer->setLocker($locker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,10 +344,12 @@ class Factory
|
||||||
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
|
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
|
||||||
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
|
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
|
||||||
$rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository');
|
$rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository');
|
||||||
|
$rm->setRepositoryClass('gitlab', 'Composer\Repository\VcsRepository');
|
||||||
$rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository');
|
$rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository');
|
||||||
$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;
|
||||||
}
|
}
|
||||||
|
@ -369,7 +378,7 @@ class Factory
|
||||||
$composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
|
$composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($io->isDebug()) {
|
if ($io->isDebug()) {
|
||||||
$io->write('Failed to initialize global composer: '.$e->getMessage());
|
$io->writeError('Failed to initialize global composer: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,8 +420,10 @@ class Factory
|
||||||
$dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
|
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
|
||||||
|
$dm->setDownloader('xz', new Downloader\XzDownloader($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;
|
||||||
}
|
}
|
||||||
|
@ -420,7 +431,6 @@ class Factory
|
||||||
/**
|
/**
|
||||||
* @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)
|
||||||
|
@ -441,11 +451,12 @@ class Factory
|
||||||
* @param IOInterface $io
|
* @param IOInterface $io
|
||||||
* @param Composer $composer
|
* @param Composer $composer
|
||||||
* @param Composer $globalComposer
|
* @param Composer $globalComposer
|
||||||
|
* @param bool $disablePlugins
|
||||||
* @return Plugin\PluginManager
|
* @return Plugin\PluginManager
|
||||||
*/
|
*/
|
||||||
protected function createPluginManager(IOInterface $io, Composer $composer, Composer $globalComposer = null)
|
protected function createPluginManager(IOInterface $io, Composer $composer, Composer $globalComposer = null, $disablePlugins = false)
|
||||||
{
|
{
|
||||||
return new Plugin\PluginManager($io, $composer, $globalComposer);
|
return new Plugin\PluginManager($io, $composer, $globalComposer, $disablePlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,6 +543,7 @@ class Factory
|
||||||
}
|
}
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $remoteFilesystem;
|
return $remoteFilesystem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -69,12 +70,21 @@ abstract class BaseIO implements IOInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($tokens = $config->get('gitlab-oauth')) {
|
||||||
|
foreach ($tokens as $domain => $token) {
|
||||||
|
$this->setAuthentication($domain, $token, 'oauth2');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reload http basic credentials from config if available
|
// reload http basic credentials from config if available
|
||||||
if ($creds = $config->get('http-basic')) {
|
if ($creds = $config->get('http-basic')) {
|
||||||
foreach ($creds as $domain => $cred) {
|
foreach ($creds as $domain => $cred) {
|
||||||
$this->setAuthentication($domain, $cred['username'], $cred['password']);
|
$this->setAuthentication($domain, $cred['username'], $cred['password']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup process timeout
|
||||||
|
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue