Merge branch 'master' of https://github.com/composer/composer
commit
1cf92eb6c8
|
@ -97,6 +97,10 @@ 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
|
||||||
|
the [Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html)
|
||||||
|
which we borrowed from Symfony.
|
||||||
|
|
||||||
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 issues](http://github.com/composer/composer/issues).
|
||||||
|
|
||||||
Community
|
Community
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0",
|
"php": ">=5.3.0",
|
||||||
"symfony/console": "2.1.0-dev",
|
"symfony/console": "dev-master",
|
||||||
"symfony/finder": ">2.0,<2.2-dev",
|
"symfony/finder": "dev-master",
|
||||||
"symfony/process": ">2.0,<2.2-dev"
|
"symfony/process": "dev-master"
|
||||||
},
|
},
|
||||||
"recommend": {
|
"recommend": {
|
||||||
"ext-zip": "*"
|
"ext-zip": "*"
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
{
|
{
|
||||||
"hash": "9c243b2c15fdc7c3e35c5200d704ba53",
|
"hash": "4ba2fad397e186b6bc453b4417c2ab00",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"package": "symfony\/process",
|
"package": "symfony/console",
|
||||||
"version": "2.1.0-dev"
|
"version": "dev-master",
|
||||||
|
"source-reference": "75ca31776bd98ad427f759cbe8a62400e40c73a1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "symfony\/finder",
|
"package": "symfony/finder",
|
||||||
"version": "2.1.0-dev"
|
"version": "dev-master",
|
||||||
|
"source-reference": "dd56fc9f1f0baa006d7491d5c17eb3e2dd8a066c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "symfony\/console",
|
"package": "symfony/process",
|
||||||
"version": "2.1.0-dev"
|
"version": "dev-master",
|
||||||
|
"source-reference": "f381eeee3733ca0fd374491fab56dce0f3ca8e34"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Composer is a tool for dependency management in PHP. It allows you to declare
|
||||||
|
the dependencies of your project and will install these dependencies for you.
|
||||||
|
|
||||||
|
## Dependency management
|
||||||
|
|
||||||
|
One important distinction to make is that composer is not a package manager. It
|
||||||
|
deals with packages, but it manages them on a per-project basis. By default it
|
||||||
|
will never install anything globally. Thus, it is a dependency manager.
|
||||||
|
|
||||||
|
This idea is not new by any means. Composer is strongly inspired by
|
||||||
|
node's [npm](http://npmjs.org/) and ruby's [bundler](http://gembundler.com/).
|
||||||
|
But there has not been such a tool for PHP so far.
|
||||||
|
|
||||||
|
The problem that composer solves is the following. You have a project that
|
||||||
|
depends on a number of libraries. Some of those libraries have dependencies of
|
||||||
|
their own. You declare the things you depend on. Composer will then go ahead
|
||||||
|
and find out which versions of which packages need to be installed, and
|
||||||
|
install them.
|
||||||
|
|
||||||
|
## 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.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We are simply stating that our project requires the `monolog/monolog` package,
|
||||||
|
any version beginning with `1.0`.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To actually get it, we need to do two things. The first one is installing
|
||||||
|
composer:
|
||||||
|
|
||||||
|
$ curl -s http://getcomposer.org/installer | php
|
||||||
|
|
||||||
|
This will just check a few PHP settings and then download `composer.phar` to
|
||||||
|
your working directory. This is the composer binary.
|
||||||
|
|
||||||
|
After that we run the command for installing all dependencies:
|
||||||
|
|
||||||
|
$ php composer.phar install
|
||||||
|
|
||||||
|
This will download monolog and dump it into `vendor/monolog/monolog`.
|
||||||
|
|
||||||
|
## Autoloading
|
||||||
|
|
||||||
|
After this you can just add the following line to your bootstrap code to get
|
||||||
|
autoloading:
|
||||||
|
|
||||||
|
```php
|
||||||
|
require 'vendor/.composer/autoload.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
That's all it takes to have a basic setup.
|
|
@ -0,0 +1,189 @@
|
||||||
|
# Basic usage
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install composer, simply run this command on the command line:
|
||||||
|
|
||||||
|
$ curl -s http://getcomposer.org/installer | php
|
||||||
|
|
||||||
|
This will perform some checks on your environment to make sure you can
|
||||||
|
actually run it.
|
||||||
|
|
||||||
|
This will download `composer.phar` and place it in your working directory.
|
||||||
|
`composer.phar` is the composer binary. It is a PHAR (PHP archive), which
|
||||||
|
is an archive format for PHP which can be run on the command line, amongst
|
||||||
|
other things.
|
||||||
|
|
||||||
|
You can place this file anywhere you wish. If you put it in your `PATH`,
|
||||||
|
you can access it globally. On unixy systems you can even make it
|
||||||
|
executable and invoke it without `php`.
|
||||||
|
|
||||||
|
To check if composer is working, just run the PHAR through `php`:
|
||||||
|
|
||||||
|
$ php composer.phar
|
||||||
|
|
||||||
|
This should give you a list of available commands.
|
||||||
|
|
||||||
|
> **Note:** You can also perform the checks only without downloading composer
|
||||||
|
> by using the `--check` option. For more information, just use `--help`.
|
||||||
|
>
|
||||||
|
> $ curl -s http://getcomposer.org/installer | php -- --help
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
|
||||||
|
To start using composer in your project, all you need is a `composer.json`
|
||||||
|
file. This file describes the dependencies of your project and may contain
|
||||||
|
other metadata as well.
|
||||||
|
|
||||||
|
The [JSON format](http://json.org/) is quite easy to write. It allows you to
|
||||||
|
define nested structures.
|
||||||
|
|
||||||
|
The first (and often only) thing you specify in `composer.json` is the
|
||||||
|
`require` key. You're simply telling composer which packages your project
|
||||||
|
depends on.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "1.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, `require` takes an object that maps package names to versions.
|
||||||
|
|
||||||
|
## Package names
|
||||||
|
|
||||||
|
The package name consists of a vendor name and the project's name. Often these
|
||||||
|
will be identical. The vendor name exists to prevent naming clashes. It allows
|
||||||
|
two different people to create a library named `json`, which would then just be
|
||||||
|
named `igorw/json` and `seldaek/json`.
|
||||||
|
|
||||||
|
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
|
||||||
|
allows adding more related projects under the same namespace later on. If you
|
||||||
|
are maintaining a library, this would make it really easy to split it up into
|
||||||
|
smaller decoupled parts.
|
||||||
|
|
||||||
|
## Package versions
|
||||||
|
|
||||||
|
We are also requiring the version `1.0.*` of monolog. This means any version
|
||||||
|
in the `1.0` development branch. It would match `1.0.0`, `1.0.2` and `1.0.20`.
|
||||||
|
|
||||||
|
Version constraints can be specified in a few different ways.
|
||||||
|
|
||||||
|
* **Exact version:** You can specify the exact version of a package, for
|
||||||
|
example `1.0.2`. This is not used very often, but can be useful.
|
||||||
|
|
||||||
|
* **Range:** By using comparison operators you can specify ranges of valid
|
||||||
|
versions. Valid operators are `>`, `>=`, `<`, `<=`. An example range would be
|
||||||
|
`>=1.0`. You can define multiple of these, separated by comma: `>=1.0,<2.0`.
|
||||||
|
|
||||||
|
* **Wildcard:** You can specify a pattern with a `*` wildcard. `1.0.*` is the
|
||||||
|
equivalent of `>=1.0,<1.1-dev`.
|
||||||
|
|
||||||
|
## Installing dependencies
|
||||||
|
|
||||||
|
To fetch the defined dependencies into the local project, you simply run the
|
||||||
|
`install` command of `composer.phar`.
|
||||||
|
|
||||||
|
$ php composer.phar install
|
||||||
|
|
||||||
|
This will find the latest version of `monolog/monolog` that matches the
|
||||||
|
supplied version constraint and download it into the the `vendor` directory.
|
||||||
|
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`.
|
||||||
|
|
||||||
|
**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
|
||||||
|
code to your repository.
|
||||||
|
|
||||||
|
Another thing that the `install` command does is it adds a `composer.lock`
|
||||||
|
file into your project root.
|
||||||
|
|
||||||
|
## Lock file
|
||||||
|
|
||||||
|
After installing the dependencies, composer writes the list of the exact
|
||||||
|
versions it installed into a `composer.lock` file. This locks the project
|
||||||
|
to those specific versions.
|
||||||
|
|
||||||
|
**Commit your project's `composer.lock` into version control.**
|
||||||
|
|
||||||
|
The reason is that anyone who sets up the project should get the same version.
|
||||||
|
The `install` command will check if a lock file is present. If it is, it will
|
||||||
|
use the versions specified there. If not, it will resolve the dependencies and
|
||||||
|
create a lock file.
|
||||||
|
|
||||||
|
If any of the dependencies gets a new version, you can update to that version
|
||||||
|
by using the `update` command. This will fetch the latest matching versions and
|
||||||
|
also update the lock file.
|
||||||
|
|
||||||
|
$ php composer.phar update
|
||||||
|
|
||||||
|
## Packagist
|
||||||
|
|
||||||
|
[Packagist](http://packagist.org/) is the main composer repository. A composer
|
||||||
|
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
|
||||||
|
means that you can automatically `require` any package that is available
|
||||||
|
there.
|
||||||
|
|
||||||
|
If you go to the [packagist website](http://packagist.org/) (packagist.org),
|
||||||
|
you can browse and search for packages.
|
||||||
|
|
||||||
|
Any open source project using composer should publish their packages on
|
||||||
|
packagist.
|
||||||
|
|
||||||
|
## Autoloading
|
||||||
|
|
||||||
|
For libraries that follow the [PSR-0](https://github.com/php-fig/fig-
|
||||||
|
standards/blob/master/accepted/PSR-0.md) naming standard, composer generates a
|
||||||
|
`vendor/.composer/autoload.php` file for autoloading. You can simply include
|
||||||
|
this file and you will get autoloading for free.
|
||||||
|
|
||||||
|
```php
|
||||||
|
require 'vendor/.composer/autoload.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
This makes it really easy to use third party code, because you really just
|
||||||
|
have to add one line to `composer.json` and run `install`. For monolog, it
|
||||||
|
means that we can just start using classes from it, and they will be
|
||||||
|
autoloaded.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$log = new Monolog\Logger('name');
|
||||||
|
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Logger::WARNING));
|
||||||
|
|
||||||
|
$log->addWarning('Foo');
|
||||||
|
```
|
||||||
|
|
||||||
|
You can even add your own code to the autoloader by adding an `autoload` key
|
||||||
|
to `composer.json`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {"Acme": "src/"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a mapping from namespaces to directories. The `src` directory would be
|
||||||
|
in your project root. An example filename would be `src/Acme/Foo.php`
|
||||||
|
containing a `Acme\Foo` class.
|
||||||
|
|
||||||
|
After adding the `autoload` key, you have to re-run `install` to re-generate
|
||||||
|
the `vendor/.composer/autoload.php` file.
|
||||||
|
|
||||||
|
Including that file will also return the autoloader instance, so you can add
|
||||||
|
retrieve it and add more namespaces. This can be useful for autoloading
|
||||||
|
classes in a test suite, for example.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$loader = require 'vendor/.composer/autoload.php';
|
||||||
|
$loader->add('Acme\Test', __DIR__);
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Composer provides its own autoloader. If you don't want to use
|
||||||
|
that one, you can just include `vendor/.composer/autoload_namespaces.php`,
|
||||||
|
which returns an associative array mapping namespaces to directories.
|
|
@ -0,0 +1,168 @@
|
||||||
|
# Libraries
|
||||||
|
|
||||||
|
This chapter will tell you how to make your library installable through composer.
|
||||||
|
|
||||||
|
## Every project is a package
|
||||||
|
|
||||||
|
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
|
||||||
|
depends on other packages. The only difference between 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
|
||||||
|
this by adding a `name` to `composer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "acme/hello-world",
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "1.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case the project name is `acme/hello-world`, where `acme` is the
|
||||||
|
vendor name. Supplying a vendor name is mandatory.
|
||||||
|
|
||||||
|
> **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
|
||||||
|
convention is all lowercase and dashes for word separation.
|
||||||
|
|
||||||
|
## Specifying the version
|
||||||
|
|
||||||
|
You need to specify the version some way. Depending on the type of repository
|
||||||
|
you are using, it might be possible to omit it from `composer.json`, because
|
||||||
|
the repository is able to infer the version from elsewhere.
|
||||||
|
|
||||||
|
If you do want to specify it explicitly, you can just add a `version` field:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However if you are using git, svn or hg, you don't have to specify it.
|
||||||
|
Composer will detect versions as follows:
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
|
||||||
|
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 for RC,
|
||||||
|
beta, alpha or patch.
|
||||||
|
|
||||||
|
Here are a few examples of valid tag names:
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
v1.0.0
|
||||||
|
1.10.5-RC1
|
||||||
|
v4.4.4beta2
|
||||||
|
v2.0.0-alpha
|
||||||
|
v2.0.4-p1
|
||||||
|
|
||||||
|
> **Note:** If you specify an explicit version in `composer.json`, the tag name must match the specified version.
|
||||||
|
|
||||||
|
### Branches
|
||||||
|
|
||||||
|
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
|
||||||
|
a branch `2.0` will get a version `2.0-dev`. If the branch does not look like
|
||||||
|
a version, it will be `dev-{branchname}`. `master` results in a `dev-master`
|
||||||
|
version.
|
||||||
|
|
||||||
|
Here are some examples of version branch names:
|
||||||
|
|
||||||
|
1.0
|
||||||
|
1.*
|
||||||
|
1.1.x
|
||||||
|
1.1.*
|
||||||
|
|
||||||
|
> **Note:** When you install a dev version, it will install it from source.
|
||||||
|
See [Repositories] for more information.
|
||||||
|
|
||||||
|
## Lock file
|
||||||
|
|
||||||
|
For projects it is recommended to commit the `composer.lock` file into version
|
||||||
|
control. For libraries this is not the case. You do not want your library to
|
||||||
|
be tied to exact versions of the dependencies. It should work with any
|
||||||
|
compatible version, so make sure you specify your version constraints so that
|
||||||
|
they include all compatible versions.
|
||||||
|
|
||||||
|
**Do not commit your library's `composer.lock` into version control.**
|
||||||
|
|
||||||
|
If you are using git, add it to the `.gitignore`.
|
||||||
|
|
||||||
|
## Publishing to a VCS
|
||||||
|
|
||||||
|
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
|
||||||
|
example we will publish the `acme/hello-world` library on GitHub under
|
||||||
|
`github.com/composer/hello-world`.
|
||||||
|
|
||||||
|
Now, To test installing the `acme/hello-world` package, we create a new
|
||||||
|
project locally. We will call it `acme/blog`. This blog will depend on `acme
|
||||||
|
/hello-world`, which in turn depends on `monolog/monolog`. We can accomplish
|
||||||
|
this by creating a new `blog` directory somewhere, containing a
|
||||||
|
`composer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "acme/blog",
|
||||||
|
"require": {
|
||||||
|
"acme/hello-world": "dev-master"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The name is not needed in this case, since we don't want to publish the blog
|
||||||
|
as a library. It is added here to clarify which `composer.json` is being
|
||||||
|
described.
|
||||||
|
|
||||||
|
Now we need to tell the blog app where to find the `hello-world` dependency.
|
||||||
|
We do this by adding a package repository specification to the blog's
|
||||||
|
`composer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "acme/blog",
|
||||||
|
"repositories": {
|
||||||
|
"acme/hello-world": {
|
||||||
|
"vcs": { "url": "https://github.com/composer/hello-world" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"acme/hello-world": "dev-master"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details on how package repositories work and what other types are
|
||||||
|
available, see [Repositories].
|
||||||
|
|
||||||
|
That's all. You can now install the dependencies by running composer's
|
||||||
|
`install` command!
|
||||||
|
|
||||||
|
**Recap:** Any git/svn/hg repository containing a `composer.json` can be added
|
||||||
|
to your project by specifying the package repository and declaring the
|
||||||
|
dependency in the `require` field.
|
||||||
|
|
||||||
|
## Publishing to packagist
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[Packagist](http://packagist.org/) is the main package repository for
|
||||||
|
composer, and it is enabled by default. Anything that is published on
|
||||||
|
packagist is available automatically through composer. Since monolog
|
||||||
|
[is on packagist](http://packagist.org/packages/monolog/monolog), we can depend
|
||||||
|
on it without having to specify any additional repositories.
|
||||||
|
|
||||||
|
Assuming we want to share `hello-world` with the world, we would want to
|
||||||
|
publish it on packagist as well. And this is really easy.
|
||||||
|
|
||||||
|
You simply hit the big "Submit Package" button and sign up. Then you 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.
|
|
@ -0,0 +1,139 @@
|
||||||
|
# Command-line interface
|
||||||
|
|
||||||
|
You've already learned how to use the command-line interface to do some
|
||||||
|
things. This chapter documents all the available commands.
|
||||||
|
|
||||||
|
## init
|
||||||
|
|
||||||
|
In the [Libraries] chapter we looked at how to create a `composer.json` by
|
||||||
|
hand. There is also an `init` command available that makes it a bit easier to
|
||||||
|
do this.
|
||||||
|
|
||||||
|
When you run the command it will interactively ask you to fill in the fields,
|
||||||
|
while using some smart defaults.
|
||||||
|
|
||||||
|
$ php composer.phar init
|
||||||
|
|
||||||
|
## install
|
||||||
|
|
||||||
|
The `install` command reads the `composer.json` file from the current
|
||||||
|
directory, resolves the dependencies, and installs them into `vendor`.
|
||||||
|
|
||||||
|
$ php composer.phar install
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
resolution.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--prefer-source:** There are two ways of downloading a package: `source` and `dist`. For stable versions composer will use the `dist` by default. The `source` is a version control repository. If `--prefer-source` 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 clone of the dependency directly.
|
||||||
|
* **--dry-run:** If you want to run through an installation without actually installing a package, you can use `--dry-run`. This will simulate the installation and show you what would happen.
|
||||||
|
* **--no-install-recommends:** By default composer will install all packages that are referenced by `recommend`. By passing this option you can disable that.
|
||||||
|
* **--install-suggests:** The packages referenced by `suggest` will not be installed by default. By passing this option, you can install them.
|
||||||
|
|
||||||
|
## update
|
||||||
|
|
||||||
|
In order to get the latest versions of the dependencies and to update the
|
||||||
|
`composer.lock` file, you should use the `update` command.
|
||||||
|
|
||||||
|
$ php composer.phar update
|
||||||
|
|
||||||
|
This will resolve all dependencies of the project and write the exact versions
|
||||||
|
into `composer.lock`.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--prefer-source:** Install packages from `source` when available.
|
||||||
|
* **--dry-run:** Simulate the command without actually doing anything.
|
||||||
|
* **--no-install-recommends:** Do not install packages referenced by `recommend`.
|
||||||
|
* **--install-suggests:** Install packages referenced by `suggest`.
|
||||||
|
|
||||||
|
## search
|
||||||
|
|
||||||
|
The search command allows you to search through the current project's package
|
||||||
|
repositories. Usually this will be just packagist. You simply pass it the
|
||||||
|
terms you want to search for.
|
||||||
|
|
||||||
|
$ php composer.phar search monolog
|
||||||
|
|
||||||
|
You can also search for more than one term by passing multiple arguments.
|
||||||
|
|
||||||
|
## show
|
||||||
|
|
||||||
|
To list all of the available packages, you can use the `show` command.
|
||||||
|
|
||||||
|
$ php composer.phar show
|
||||||
|
|
||||||
|
If you want to see the details of a certain package, you can pass the package
|
||||||
|
name.
|
||||||
|
|
||||||
|
$ php composer.phar show monolog/monolog
|
||||||
|
|
||||||
|
name : monolog/monolog
|
||||||
|
versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
|
||||||
|
type : library
|
||||||
|
names : monolog/monolog
|
||||||
|
source : [git] http://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
||||||
|
dist : [zip] http://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da
|
||||||
|
license : MIT
|
||||||
|
|
||||||
|
autoload
|
||||||
|
psr-0
|
||||||
|
Monolog : src/
|
||||||
|
|
||||||
|
requires
|
||||||
|
php >=5.3.0
|
||||||
|
|
||||||
|
You can even pass the package version, which will tell you the details of that
|
||||||
|
specific version.
|
||||||
|
|
||||||
|
$ php composer.phar show monolog/monolog 1.0.2
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--installed:** Will list the packages that are installed.
|
||||||
|
* **--platform:** Will list only [Platform packages].
|
||||||
|
|
||||||
|
## depends
|
||||||
|
|
||||||
|
The `depends` command tells you which other packages depend on a certain
|
||||||
|
package. You can specify which link types (`require`, `recommend`, `suggest`)
|
||||||
|
should be included in the listing.
|
||||||
|
|
||||||
|
$ php composer.phar depends --link-type=require monolog/monolog
|
||||||
|
|
||||||
|
nrk/monolog-fluent
|
||||||
|
poc/poc
|
||||||
|
propel/propel
|
||||||
|
symfony/monolog-bridge
|
||||||
|
symfony/symfony
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* **--link-type:** The link types to match on, can be specified multiple
|
||||||
|
times.
|
||||||
|
|
||||||
|
## validate
|
||||||
|
|
||||||
|
You should always run the `validate` command before you commit your
|
||||||
|
`composer.json` file, and before you tag a release. It will check if your
|
||||||
|
`composer.json` is valid.
|
||||||
|
|
||||||
|
$ php composer.phar validate
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
$ php composer.phar self-update
|
||||||
|
|
||||||
|
## help
|
||||||
|
|
||||||
|
To get more information about a certain command, just use `help`.
|
||||||
|
|
||||||
|
$ php composer.phar help install
|
|
@ -0,0 +1,417 @@
|
||||||
|
# composer.json
|
||||||
|
|
||||||
|
This chapter will explain all of the options available in `composer.json`.
|
||||||
|
|
||||||
|
## JSON schema
|
||||||
|
|
||||||
|
We have a [JSON schema](http://json-schema.org) that documents the format and
|
||||||
|
can also be used to validate your `composer.json`. In fact, it is used by the
|
||||||
|
`validate` command. You can find it at: [`Resources/composer-
|
||||||
|
schema.json`](https://github.com/composer/composer/blob/master/res
|
||||||
|
/composer-schema.json).
|
||||||
|
|
||||||
|
## Package root
|
||||||
|
|
||||||
|
The root of the package definition is a JSON object.
|
||||||
|
|
||||||
|
## name
|
||||||
|
|
||||||
|
The name of the package. It consists of vendor name and project name,
|
||||||
|
separated by `/`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
* monolog/monolog
|
||||||
|
* igorw/event-source
|
||||||
|
|
||||||
|
Required for published packages (libraries).
|
||||||
|
|
||||||
|
## description
|
||||||
|
|
||||||
|
A short description of the package. Usually this is just one line long.
|
||||||
|
|
||||||
|
Optional but recommended.
|
||||||
|
|
||||||
|
## version
|
||||||
|
|
||||||
|
The version of the package.
|
||||||
|
|
||||||
|
This must follow the format of `X.Y.Z` with an optional suffix of `-dev`,
|
||||||
|
`alphaN`, `-betaN` or `-RCN`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
1.0.2
|
||||||
|
1.1.0
|
||||||
|
0.2.5
|
||||||
|
1.0.0-dev
|
||||||
|
1.0.0-beta2
|
||||||
|
1.0.0-RC5
|
||||||
|
|
||||||
|
Optional if the package repository can infer the version from somewhere, such
|
||||||
|
as the VCS tag name in the VCS repository. In that case it is also recommended
|
||||||
|
to omit it.
|
||||||
|
|
||||||
|
## type
|
||||||
|
|
||||||
|
The type of the package. It defaults to `library`.
|
||||||
|
|
||||||
|
Package types are used for custom installation logic. If you have a package
|
||||||
|
that needs some special logic, you can define a custom type. This could be a
|
||||||
|
`symfony-bundle`, a `wordpress-plugin` or a `typo3-module`. These will all be
|
||||||
|
specific to certain projects, and they will need to provide an installer
|
||||||
|
capable of installing packages of that type.
|
||||||
|
|
||||||
|
Out of the box, composer supports two types:
|
||||||
|
|
||||||
|
* **library:** This is the default. It will simply copy the files to `vendor`.
|
||||||
|
* **composer-installer:** A package of type `composer-installer` provides an
|
||||||
|
installer for other packages that have a custom type. Symfony could supply a
|
||||||
|
`symfony/bundle-installer` package, which every bundle would depend on.
|
||||||
|
Whenever you install a bundle, it will fetch the installer and register it, in
|
||||||
|
order to be able to install the bundle.
|
||||||
|
|
||||||
|
Only use a custom type if you need custom logic during installation. It is
|
||||||
|
recommended to omit this field and have it just default to `library`.
|
||||||
|
|
||||||
|
## keywords
|
||||||
|
|
||||||
|
An array of keywords that the package is related to. These can be used for
|
||||||
|
searching and filtering.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
logging
|
||||||
|
events
|
||||||
|
database
|
||||||
|
redis
|
||||||
|
templating
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## homepage
|
||||||
|
|
||||||
|
An URL to the website of the project.
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## time
|
||||||
|
|
||||||
|
Release date of the version.
|
||||||
|
|
||||||
|
Must be in `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` format.
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## license
|
||||||
|
|
||||||
|
The license of the package. This can be either a string or an array of strings.
|
||||||
|
|
||||||
|
The recommended notation for the most common licenses is:
|
||||||
|
|
||||||
|
MIT
|
||||||
|
BSD-2
|
||||||
|
BSD-3
|
||||||
|
BSD-4
|
||||||
|
GPLv2
|
||||||
|
GPLv3
|
||||||
|
LGPLv2
|
||||||
|
LGPLv3
|
||||||
|
Apache2
|
||||||
|
WTFPL
|
||||||
|
|
||||||
|
Optional, but it is highly recommended to supply this.
|
||||||
|
|
||||||
|
## authors
|
||||||
|
|
||||||
|
The authors of the package. This is an array of objects.
|
||||||
|
|
||||||
|
Each author object can have following properties:
|
||||||
|
|
||||||
|
* **name:** The author's name. Usually his real name.
|
||||||
|
* **email:** The author's email address.
|
||||||
|
* **homepage:** An URL to the author's website.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nils Adermann",
|
||||||
|
"email": "naderman@naderman.de",
|
||||||
|
"homepage": "http://www.naderman.de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "http://seld.be"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional, but highly recommended.
|
||||||
|
|
||||||
|
## Link types
|
||||||
|
|
||||||
|
Each of these takes an object which maps package names to version constraints.
|
||||||
|
|
||||||
|
* **require:** Packages required by this package.
|
||||||
|
* **recommend:** Recommended packages, installed by default.
|
||||||
|
* **suggest:** Suggested packages. These are displayed after installation,
|
||||||
|
but not installed by default.
|
||||||
|
* **conflict:** Mark this version of this package as conflicting with other
|
||||||
|
packages.
|
||||||
|
* **replace:** Packages that can be replaced by this package. This is useful
|
||||||
|
for large repositories with subtree splits. It allows the main package to
|
||||||
|
replace all of it's child packages.
|
||||||
|
* **provide:** List of other packages that are provided by this package. This
|
||||||
|
is mostly useful for common interfaces. A package could depend on some virtual
|
||||||
|
`logger` package, any library that provides this logger, would simply list it
|
||||||
|
in `provide`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "1.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## autoload
|
||||||
|
|
||||||
|
Autoload mapping for a PHP autoloader.
|
||||||
|
|
||||||
|
Currently only [PSR-0](https://github.com/php-fig/fig-
|
||||||
|
standards/blob/master/accepted/PSR-0.md) autoloading is supported. Under the
|
||||||
|
`psr-0` key you define a mapping from namespaces to paths, relative to the
|
||||||
|
package root.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": { "Monolog": "src/" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional, but it is highly recommended that you follow PSR-0 and use this.
|
||||||
|
|
||||||
|
## target-dir
|
||||||
|
|
||||||
|
Defines the installation target.
|
||||||
|
|
||||||
|
In case the package root is below the namespace declaration you cannot
|
||||||
|
autoload properly. `target-dir` solves this problem.
|
||||||
|
|
||||||
|
An example is Symfony. There are individual packages for the components. The
|
||||||
|
Yaml component is under `Symfony\Component\Yaml`. The package root is that
|
||||||
|
`Yaml` directory. To make autoloading possible, we need to make sure that it
|
||||||
|
is not installed into `vendor/symfony/yaml`, but instead into
|
||||||
|
`vendor/symfony/yaml/Symfony/Component/Yaml`, so that the autoloader can load
|
||||||
|
it from `vendor/symfony/yaml`.
|
||||||
|
|
||||||
|
To do that, `autoload` and `target-dir` are defined as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": { "Symfony\\Component\\Yaml": "" }
|
||||||
|
},
|
||||||
|
"target-dir": "Symfony/Component/Yaml"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## repositories
|
||||||
|
|
||||||
|
Custom package repositories to use.
|
||||||
|
|
||||||
|
By default composer just uses the packagist repository. By specifying
|
||||||
|
repositories you can get packages from elsewhere.
|
||||||
|
|
||||||
|
Repositories are not resolved recursively. You can only add them to your main
|
||||||
|
`composer.json`. Repository declarations of dependencies' `composer.json`s are
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
Following repository types are supported:
|
||||||
|
|
||||||
|
* **composer:** A composer repository is simply a `packages.json` file served
|
||||||
|
via HTTP that contains a list of `composer.json` objects with additional
|
||||||
|
`dist` and/or `source` information.
|
||||||
|
* **vcs:** The version control system repository can fetch packages from git,
|
||||||
|
svn and hg repositories. Note the distinction between package repository and
|
||||||
|
version control repository.
|
||||||
|
* **pear:** With this you can import any pear repository into your composer
|
||||||
|
project.
|
||||||
|
* **package:** If you depend on a project that does not have any support for
|
||||||
|
composer whatsoever you can define the package inline using a `package`
|
||||||
|
repository. You basically just inline the `composer.json` object.
|
||||||
|
|
||||||
|
For more information on any of these, see [Repositories].
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "http://packages.example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "https://github.com/Seldaek/monolog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "pear",
|
||||||
|
"url": "http://pear2.php.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": {
|
||||||
|
"name": "smarty/smarty",
|
||||||
|
"version": "3.1.7",
|
||||||
|
"dist": {
|
||||||
|
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||||
|
"type": "zip"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"url": "http://smarty-php.googlecode.com/svn/",
|
||||||
|
"type": "svn",
|
||||||
|
"reference": "tags/Smarty_3_1_7/distribution/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Order is significant here. Repositories added later will take
|
||||||
|
precedence. This also means that custom repositories can override packages
|
||||||
|
that exist on packagist.
|
||||||
|
|
||||||
|
You can also disable the packagist repository by setting `packagist` to
|
||||||
|
`false`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"packagist": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## config
|
||||||
|
|
||||||
|
A set of configuration options. It is only used for projects.
|
||||||
|
|
||||||
|
The following options are supported:
|
||||||
|
|
||||||
|
* **vendor-dir:** Defaults to `vendor`. You can install dependencies into a
|
||||||
|
different directory if you want to.
|
||||||
|
* **bin-dir:** Defaults to `vendor/bin`. If a project includes binaries, they
|
||||||
|
will be symlinked into this directory.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"bin-dir": "bin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## scripts
|
||||||
|
|
||||||
|
Composer allows you to hook into various parts of the installation process through the use of scripts.
|
||||||
|
|
||||||
|
These events are supported:
|
||||||
|
|
||||||
|
* **pre-install-cmd:** Occurs before the install command is executed, contains
|
||||||
|
one or more Class::method callables.
|
||||||
|
* **post-install-cmd:** Occurs after the install command is executed, contains
|
||||||
|
one or more Class::method callables.
|
||||||
|
* **pre-update-cmd:** Occurs before the update command is executed, contains
|
||||||
|
one or more Class::method callables.
|
||||||
|
* **post-update-cmd:** Occurs after the update command is executed, contains
|
||||||
|
one or more Class::method callables.
|
||||||
|
* **pre-package-install:** Occurs before a package is installed, contains one
|
||||||
|
or more Class::method callables.
|
||||||
|
* **post-package-install:** Occurs after a package is installed, contains one
|
||||||
|
or more Class::method callables.
|
||||||
|
* **pre-package-update:** Occurs before a package is updated, contains one or
|
||||||
|
more Class::method callables.
|
||||||
|
* **post-package-update:** Occurs after a package is updated, contains one or
|
||||||
|
more Class::method callables.
|
||||||
|
* **pre-package-uninstall:** Occurs before a package has been uninstalled,
|
||||||
|
contains one or more Class::method callables.
|
||||||
|
* **post-package-uninstall:** Occurs after a package has been uninstalled,
|
||||||
|
contains one or more Class::method callables.
|
||||||
|
|
||||||
|
For each of these events you can provide a static method on a class that will
|
||||||
|
handle it.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"post-install-cmd": [
|
||||||
|
"Acme\\ScriptHandler::doSomething"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The event handler receives a `Composer\Script\Event` object as an argument,
|
||||||
|
which gives you access to the `Composer\Composer` instance through the
|
||||||
|
`getComposer` method.
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace Acme;
|
||||||
|
|
||||||
|
use Composer\Script\Event;
|
||||||
|
|
||||||
|
class ScriptHandler
|
||||||
|
{
|
||||||
|
static public function doSomething(Event $event)
|
||||||
|
{
|
||||||
|
// custom logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## extra
|
||||||
|
|
||||||
|
Arbitrary extra data for consumption by `scripts`.
|
||||||
|
|
||||||
|
This can be virtually anything. To access it from within a script event
|
||||||
|
handler, you can do:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$extra = $event->getComposer()->getPackage()->getExtra();
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional.
|
||||||
|
|
||||||
|
## bin
|
||||||
|
|
||||||
|
A set of files that should be treated as binaries and symlinked into the `bin-
|
||||||
|
dir` (from config).
|
||||||
|
|
||||||
|
See [articles/bin.md] for more details.
|
||||||
|
|
||||||
|
Optional.
|
|
@ -0,0 +1,263 @@
|
||||||
|
# Repositories
|
||||||
|
|
||||||
|
This chapter will explain the concept of packages and repositories, what kinds
|
||||||
|
of repositories are available, and how they work.
|
||||||
|
|
||||||
|
## Concepts
|
||||||
|
|
||||||
|
Before we look at the different types of repositories that we can have, we
|
||||||
|
need to understand some of the basic concepts that composer is built on.
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
Composer is a dependency manager. It installs packages. A package is
|
||||||
|
essentially just a directory containing something. In this case it is PHP
|
||||||
|
code, but in theory it could be anything. And it contains a package
|
||||||
|
description which has a name and a version. The name and the version are used
|
||||||
|
to identify the package.
|
||||||
|
|
||||||
|
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
|
||||||
|
important when you want to change it.
|
||||||
|
|
||||||
|
In addition to the name and the version, there is useful data. The only really
|
||||||
|
important piece of information is the package source, that describes where to
|
||||||
|
get the package contents. The package data points to the contents of the
|
||||||
|
package. And there are two options here: dist and source.
|
||||||
|
|
||||||
|
**Dist:** The dist is a packaged version of the package data. Usually a
|
||||||
|
released version, usually a stable release.
|
||||||
|
|
||||||
|
**Source:** The source is used for development. This will usually originate
|
||||||
|
from a source code repository, such as git. You can fetch this when you want
|
||||||
|
to modify the downloaded package.
|
||||||
|
|
||||||
|
Packages can supply either of these, or even both. Depending on certain
|
||||||
|
factors, such as user-supplied options and stability of the package, one will
|
||||||
|
be preferred.
|
||||||
|
|
||||||
|
### Repository
|
||||||
|
|
||||||
|
A repository is a package source. It's a list of packages, of which you can
|
||||||
|
pick some to install.
|
||||||
|
|
||||||
|
You can also add more repositories to your project by declaring them in
|
||||||
|
`composer.json`.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### Composer
|
||||||
|
|
||||||
|
The main repository type is the `composer` repository. It uses a single
|
||||||
|
`packages.json` file that contains all of the package metadata. The JSON
|
||||||
|
format is as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vendor/packageName": {
|
||||||
|
"name": "vendor/packageName",
|
||||||
|
"description": "Package description",
|
||||||
|
"versions": {
|
||||||
|
"master-dev": { @composer.json },
|
||||||
|
"1.0.0": { @composer.json }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `@composer.json` marker would be the contents of the `composer.json` from
|
||||||
|
that package version including as a minimum:
|
||||||
|
|
||||||
|
* name
|
||||||
|
* version
|
||||||
|
* dist or source
|
||||||
|
|
||||||
|
Here is a minimal package definition:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "smarty/smarty",
|
||||||
|
"version": "3.1.7",
|
||||||
|
"dist": {
|
||||||
|
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||||
|
"type": "zip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It may include any of the other fields specified in the [schema].
|
||||||
|
|
||||||
|
The `composer` repository is also what packagist uses. To reference a
|
||||||
|
`composer` repository, just supply the path before the `packages.json` file.
|
||||||
|
In case of packagist, that file is located at `/packages.json`, so the URL of
|
||||||
|
the repository would be `http://packagist.org`. For
|
||||||
|
`http://example.org/packages.org` the repository URL would be
|
||||||
|
`http://example.org`.
|
||||||
|
|
||||||
|
### VCS
|
||||||
|
|
||||||
|
VCS stands for version control system. This includes versioning systems like
|
||||||
|
git, svn or hg. Composer has a repository type for installing packages from
|
||||||
|
these systems.
|
||||||
|
|
||||||
|
There are a few use cases for this. The most common one is maintaining your
|
||||||
|
own fork of a third party library. If you are using a certain library for your
|
||||||
|
project and you decide to change something in the library, you will want your
|
||||||
|
project to use the patched version. If the library is on GitHub (this is the
|
||||||
|
case most of the time), you can simply fork it there and push your changes to
|
||||||
|
your fork. After that you update the project's `composer.json`. All you have
|
||||||
|
to do is add your fork as a repository and update the version constraint to
|
||||||
|
point to your custom branch.
|
||||||
|
|
||||||
|
Example assuming you patched monolog to fix a bug in the `bugfix` branch:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "http://github.com/igorw/monolog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "dev-bugfix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you run `php composer.phar update`, you should get your modified version
|
||||||
|
of `monolog/monolog` instead of the one from packagist.
|
||||||
|
|
||||||
|
Git is not the only version control system supported by the VCS repository.
|
||||||
|
The following are supported:
|
||||||
|
|
||||||
|
* **Git:** [git-scm.com](http://git-scm.com)
|
||||||
|
* **Subversion:** [subversion.apache.org](http://subversion.apache.org)
|
||||||
|
* **Mercurial:** [mercurial.selenic.com](http://mercurial.selenic.com)
|
||||||
|
|
||||||
|
To use these systems you need to have them installed. That can be
|
||||||
|
invonvenient. And for this reason there is special support for GitHub and
|
||||||
|
BitBucket that use the APIs provided by these sites, to fetch the packages
|
||||||
|
without having to install the version control system. The VCS repository
|
||||||
|
provides `dist`s for them that fetch the packages as zips.
|
||||||
|
|
||||||
|
* **GitHub:** [github.com](https://github.com) (Git)
|
||||||
|
* **BitBucket:** [bitbucket.org](https://bitbucket.org) (Git and Mercurial)
|
||||||
|
|
||||||
|
The VCS driver to be used is detected automatically based on the URL.
|
||||||
|
|
||||||
|
### PEAR
|
||||||
|
|
||||||
|
It is possible to install packages from any PEAR channel by using the `pear`
|
||||||
|
repository. Composer will prefix all package names with `pear-{channelName}/` to
|
||||||
|
avoid conflicts.
|
||||||
|
|
||||||
|
Example using `pear2.php.net`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "pear",
|
||||||
|
"url": "http://pear2.php.net"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"pear-pear2/PEAR2_HTTP_Request": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case the short name of the channel is `pear2`, so the
|
||||||
|
`PEAR2_HTTP_Request` package name becomes `pear-pear2/PEAR2_HTTP_Request`.
|
||||||
|
|
||||||
|
> **Note:** The `pear` repository requires doing quite a few requests per
|
||||||
|
> package, so this may considerably slow down the installation process.
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
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 using a `package`
|
||||||
|
repository.
|
||||||
|
|
||||||
|
Basically, you define the same information that is included in the `composer`
|
||||||
|
repository's `packages.json`, but only for a single package. Again, the
|
||||||
|
minimally required fields are `name`, `version`, and either of `dist` or
|
||||||
|
`source`.
|
||||||
|
|
||||||
|
Here is an example for the smarty template engine:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": {
|
||||||
|
"name": "smarty/smarty",
|
||||||
|
"version": "3.1.7",
|
||||||
|
"dist": {
|
||||||
|
"url": "http://www.smarty.net/files/Smarty-3.1.7.zip",
|
||||||
|
"type": "zip"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"url": "http://smarty-php.googlecode.com/svn/",
|
||||||
|
"type": "svn",
|
||||||
|
"reference": "tags/Smarty_3_1_7/distribution/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"smarty/smarty": "3.1.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Typically you would leave the source part off, as you don't really need it.
|
||||||
|
|
||||||
|
## Hosting your own
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* **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.
|
||||||
|
|
||||||
|
* **Separate ecosystem:** If you have a project which has its own ecosystem,
|
||||||
|
and the packages aren't really reusable by the greater PHP community, you
|
||||||
|
might want to keep them separate to packagist. An example of this would be
|
||||||
|
wordpress plugins.
|
||||||
|
|
||||||
|
When hosting your own package repository it is recommended to use a `composer`
|
||||||
|
one. This is type that is native to composer and yields the best performance.
|
||||||
|
|
||||||
|
There are a few different tools that can help you create a `composer`
|
||||||
|
repository.
|
||||||
|
|
||||||
|
### Packagist
|
||||||
|
|
||||||
|
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
|
||||||
|
quite straight-forward to do.
|
||||||
|
|
||||||
|
Packagist is a Symfony2 application, and it is [available on
|
||||||
|
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
|
||||||
|
of all VCS packages, periodically re-crawls them, and exposes them as a composer
|
||||||
|
repository.
|
||||||
|
|
||||||
|
To set your own copy, simply follow the instructions from the [packagist
|
||||||
|
github repository](https://github.com/composer/packagist).
|
||||||
|
|
||||||
|
### Satis
|
||||||
|
|
||||||
|
Satis is a static `composer` repository generator. It is a bit like a ultra-
|
||||||
|
lightweight, file-based version of packagist.
|
||||||
|
|
||||||
|
You give it a `composer.json` containing repositories, typically VCS and package
|
||||||
|
repository definitions. It will fetch all the packages that are `require`d from
|
||||||
|
these repositories and dump a `packages.json` that is your `composer`
|
||||||
|
repository.
|
||||||
|
|
||||||
|
Check [the satis GitHub repository](https://github.com/composer/satis) for more
|
||||||
|
information.
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Community
|
||||||
|
|
||||||
|
We have a lot of people using composer, and also many contributors to the
|
||||||
|
project.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you would like to contribute to composer, please read the
|
||||||
|
[README](https://github.com/composer/composer).
|
||||||
|
|
||||||
|
The most important guidelines are described as follows:
|
||||||
|
|
||||||
|
> 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.
|
||||||
|
>
|
||||||
|
> 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)
|
||||||
|
> which we borrowed from Symfony.
|
||||||
|
|
||||||
|
## IRC / mailing list
|
||||||
|
|
||||||
|
The developer mailing list is on [google groups](http://groups.google.com/group
|
||||||
|
/composer-dev) IRC channels are available for discussion as well, on
|
||||||
|
irc.freenode.org [#composer](irc://irc.freenode.org/composer) for users and
|
||||||
|
[#composer-dev](irc://irc.freenode.org/composer-dev) for development.
|
|
@ -101,6 +101,20 @@
|
||||||
"description": "This is a hash of package name (keys) and version constraints (values) that this package suggests work well with it (typically this will only be suggested to the user).",
|
"description": "This is a hash of package name (keys) and version constraints (values) that this package suggests work well with it (typically this will only be suggested to the user).",
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
},
|
},
|
||||||
|
"config": {
|
||||||
|
"type": ["object"],
|
||||||
|
"description": "Composer options.",
|
||||||
|
"properties": {
|
||||||
|
"vendor-dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The location where all packages are installed, defaults to \"vendor\"."
|
||||||
|
},
|
||||||
|
"bin-dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The location where all binaries are linked, defaults to \"vendor/bin\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"type": ["object", "array"],
|
"type": ["object", "array"],
|
||||||
"description": "Arbitrary extra data that can be used by custom installers, for example, package of type composer-installer must have a 'class' key defining the installer class name.",
|
"description": "Arbitrary extra data that can be used by custom installers, for example, package of type composer-installer must have a 'class' key defining the installer class name.",
|
||||||
|
@ -121,6 +135,59 @@
|
||||||
"type": ["object", "array"],
|
"type": ["object", "array"],
|
||||||
"description": "A set of additional repositories where packages can be found.",
|
"description": "A set of additional repositories where packages can be found.",
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"type": ["array"],
|
||||||
|
"description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"type": ["object"],
|
||||||
|
"description": "Scripts listeners that will be executed before/after some events.",
|
||||||
|
"properties": {
|
||||||
|
"pre-install-cmd": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs before the install command is executed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"post-install-cmd": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs after the install command is executed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"pre-update-cmd": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs before the update command is executed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"post-update-cmd": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs after the update command is executed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"pre-package-install": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs before a package is installed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"post-package-install": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs after a package is installed, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"pre-package-update": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs before a package is updated, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"post-package-update": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs after a package is updated, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"pre-package-uninstall": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs before a package has been uninstalled, contains one or more Class::method callables."
|
||||||
|
},
|
||||||
|
"post-package-uninstall": {
|
||||||
|
"type": ["array", "string"],
|
||||||
|
"description": "Occurs after a package has been uninstalled, contains one or more Class::method callables."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,8 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
*/
|
*/
|
||||||
class DependsCommand extends Command
|
class DependsCommand extends Command
|
||||||
{
|
{
|
||||||
|
protected $linkTypes = array('require', 'recommend', 'suggest');
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
|
@ -32,7 +34,7 @@ class DependsCommand extends Command
|
||||||
->setDescription('Shows which packages depend on the given package')
|
->setDescription('Shows which packages depend on the given package')
|
||||||
->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_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Link types to show', array('requires', 'recommends', 'suggests'))
|
new InputOption('link-type', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Link types to show', $this->linkTypes)
|
||||||
))
|
))
|
||||||
->setHelp(<<<EOT
|
->setHelp(<<<EOT
|
||||||
Displays detailed information about where a package is referenced.
|
Displays detailed information about where a package is referenced.
|
||||||
|
@ -74,10 +76,15 @@ EOT
|
||||||
|
|
||||||
$repos = $composer->getRepositoryManager()->getRepositories();
|
$repos = $composer->getRepositoryManager()->getRepositories();
|
||||||
$types = $input->getOption('link-type');
|
$types = $input->getOption('link-type');
|
||||||
|
|
||||||
foreach ($repos as $repository) {
|
foreach ($repos as $repository) {
|
||||||
foreach ($repository->getPackages() as $package) {
|
foreach ($repository->getPackages() as $package) {
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
foreach ($package->{'get'.$type}() as $link) {
|
$type = rtrim($type, 's');
|
||||||
|
if (!in_array($type, $this->linkTypes)) {
|
||||||
|
throw new \InvalidArgumentException('Unexpected link type: '.$type.', valid types: '.implode(', ', $this->linkTypes));
|
||||||
|
}
|
||||||
|
foreach ($package->{'get'.$type.'s'}() as $link) {
|
||||||
if ($link->getTarget() === $needle) {
|
if ($link->getTarget() === $needle) {
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
$references[] = array($type, $package, $link);
|
$references[] = array($type, $package, $link);
|
||||||
|
|
|
@ -30,6 +30,7 @@ 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;
|
||||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||||
|
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||||
use Composer\DependencyResolver\Solver;
|
use Composer\DependencyResolver\Solver;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
|
||||||
|
@ -113,23 +114,20 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
// creating requirements request
|
// creating requirements request
|
||||||
|
$installFromLock = false;
|
||||||
$request = new Request($pool);
|
$request = new Request($pool);
|
||||||
if ($update) {
|
if ($update) {
|
||||||
$io->write('<info>Updating dependencies</info>');
|
$io->write('<info>Updating dependencies</info>');
|
||||||
$installedPackages = $installedRepo->getPackages();
|
$installedPackages = $installedRepo->getPackages();
|
||||||
$links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests);
|
$links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests);
|
||||||
|
|
||||||
foreach ($links as $link) {
|
$request->updateAll();
|
||||||
foreach ($installedPackages as $package) {
|
|
||||||
if ($package->getName() === $link->getTarget()) {
|
|
||||||
$request->update($package->getName(), new VersionConstraint('=', $package->getVersion()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
$request->install($link->getTarget(), $link->getConstraint());
|
$request->install($link->getTarget(), $link->getConstraint());
|
||||||
}
|
}
|
||||||
} elseif ($composer->getLocker()->isLocked()) {
|
} elseif ($composer->getLocker()->isLocked()) {
|
||||||
|
$installFromLock = true;
|
||||||
$io->write('<info>Installing from lock file</info>');
|
$io->write('<info>Installing from lock file</info>');
|
||||||
|
|
||||||
if (!$composer->getLocker()->isFresh()) {
|
if (!$composer->getLocker()->isFresh()) {
|
||||||
|
@ -158,45 +156,63 @@ EOT
|
||||||
// solve dependencies
|
// solve dependencies
|
||||||
$operations = $solver->solve($request);
|
$operations = $solver->solve($request);
|
||||||
|
|
||||||
// check for missing deps
|
|
||||||
// TODO this belongs in the solver, but this will do for now to report top-level deps missing at least
|
|
||||||
foreach ($request->getJobs() as $job) {
|
|
||||||
if ('install' === $job['cmd']) {
|
|
||||||
foreach ($installedRepo->getPackages() as $package ) {
|
|
||||||
if ($installedRepo->hasPackage($package) && !$package->isPlatform() && !$installationManager->isPackageInstalled($package)) {
|
|
||||||
$operations[$job['packageName']] = new InstallOperation($package, Solver::RULE_PACKAGE_NOT_EXIST);
|
|
||||||
}
|
|
||||||
if (in_array($job['packageName'], $package->getNames())) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($operations as $operation) {
|
|
||||||
if ('install' === $operation->getJobType() && in_array($job['packageName'], $operation->getPackage()->getNames())) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
if ('update' === $operation->getJobType() && in_array($job['packageName'], $operation->getTargetPackage()->getNames())) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($pool->whatProvides($job['packageName'])) {
|
|
||||||
throw new \UnexpectedValueException('Package '.$job['packageName'].' can not be installed, either because its version constraint is incorrect, or because one of its dependencies was not found.');
|
|
||||||
}
|
|
||||||
throw new \UnexpectedValueException('Package '.$job['packageName'].' was not found in the package pool, check the name for typos.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute operations
|
// execute operations
|
||||||
if (!$operations) {
|
if (!$operations) {
|
||||||
$io->write('<info>Nothing to install/update</info>');
|
$io->write('<info>Nothing to install/update</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// force dev packages to be updated to latest reference on update
|
||||||
|
if ($update) {
|
||||||
|
foreach ($localRepo->getPackages() as $package) {
|
||||||
|
// skip non-dev packages
|
||||||
|
if (!$package->isDev()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip packages that will be updated/uninstalled
|
||||||
|
foreach ($operations as $operation) {
|
||||||
|
if (('update' === $operation->getJobType() && $package === $operation->getInitialPackage())
|
||||||
|
|| ('uninstall' === $operation->getJobType() && $package === $operation->getPackage())
|
||||||
|
) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// force update
|
||||||
|
$newPackage = $composer->getRepositoryManager()->findPackage($package->getName(), $package->getVersion());
|
||||||
|
if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) {
|
||||||
|
$operations[] = new UpdateOperation($package, $newPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($operations as $operation) {
|
foreach ($operations as $operation) {
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
$io->write((string) $operation);
|
$io->write((string) $operation);
|
||||||
}
|
}
|
||||||
if (!$dryRun) {
|
if (!$dryRun) {
|
||||||
$eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
$eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
||||||
|
|
||||||
|
// if installing from lock, restore dev packages' references to their locked state
|
||||||
|
if ($installFromLock) {
|
||||||
|
$package = null;
|
||||||
|
if ('update' === $operation->getJobType()) {
|
||||||
|
$package = $operation->getTargetPackage();
|
||||||
|
} elseif ('install' === $operation->getJobType()) {
|
||||||
|
$package = $operation->getPackage();
|
||||||
|
}
|
||||||
|
if ($package && $package->isDev()) {
|
||||||
|
$lockData = $composer->getLocker()->getLockData();
|
||||||
|
foreach ($lockData['packages'] as $lockedPackage) {
|
||||||
|
if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) {
|
||||||
|
$package->setSourceReference($lockedPackage['source-reference']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
$installationManager->execute($operation);
|
$installationManager->execute($operation);
|
||||||
|
|
||||||
$eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
$eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,6 @@ use Composer\Package\LinkConstraint\VersionConstraint;
|
||||||
*/
|
*/
|
||||||
class DefaultPolicy implements PolicyInterface
|
class DefaultPolicy implements PolicyInterface
|
||||||
{
|
{
|
||||||
public function allowUninstall()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator)
|
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator)
|
||||||
{
|
{
|
||||||
$constraint = new VersionConstraint($operator, $b->getVersion());
|
$constraint = new VersionConstraint($operator, $b->getVersion());
|
||||||
|
|
|
@ -20,7 +20,6 @@ use Composer\Package\PackageInterface;
|
||||||
*/
|
*/
|
||||||
interface PolicyInterface
|
interface PolicyInterface
|
||||||
{
|
{
|
||||||
function allowUninstall();
|
|
||||||
function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
|
function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
|
||||||
function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
|
function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
|
||||||
function installable(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
|
function installable(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
|
||||||
|
|
|
@ -55,6 +55,11 @@ class Request
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateAll()
|
||||||
|
{
|
||||||
|
$this->jobs[] = array('cmd' => 'update-all', 'packages' => array());
|
||||||
|
}
|
||||||
|
|
||||||
public function getJobs()
|
public function getJobs()
|
||||||
{
|
{
|
||||||
return $this->jobs;
|
return $this->jobs;
|
||||||
|
|
|
@ -20,7 +20,6 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
// highest priority => lowest number
|
// highest priority => lowest number
|
||||||
const TYPE_PACKAGE = 0;
|
const TYPE_PACKAGE = 0;
|
||||||
const TYPE_JOB = 1;
|
const TYPE_JOB = 1;
|
||||||
const TYPE_UPDATE = 2;
|
|
||||||
const TYPE_FEATURE = 3;
|
const TYPE_FEATURE = 3;
|
||||||
const TYPE_CHOICE = 4;
|
const TYPE_CHOICE = 4;
|
||||||
const TYPE_LEARNED = 5;
|
const TYPE_LEARNED = 5;
|
||||||
|
@ -29,7 +28,6 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
-1 => 'UNKNOWN',
|
-1 => 'UNKNOWN',
|
||||||
self::TYPE_PACKAGE => 'PACKAGE',
|
self::TYPE_PACKAGE => 'PACKAGE',
|
||||||
self::TYPE_FEATURE => 'FEATURE',
|
self::TYPE_FEATURE => 'FEATURE',
|
||||||
self::TYPE_UPDATE => 'UPDATE',
|
|
||||||
self::TYPE_JOB => 'JOB',
|
self::TYPE_JOB => 'JOB',
|
||||||
self::TYPE_CHOICE => 'CHOICE',
|
self::TYPE_CHOICE => 'CHOICE',
|
||||||
self::TYPE_LEARNED => 'LEARNED',
|
self::TYPE_LEARNED => 'LEARNED',
|
||||||
|
|
|
@ -52,7 +52,6 @@ class Solver
|
||||||
protected $decisionMap;
|
protected $decisionMap;
|
||||||
protected $installedMap;
|
protected $installedMap;
|
||||||
|
|
||||||
protected $packageToUpdateRule = array();
|
|
||||||
protected $packageToFeatureRule = array();
|
protected $packageToFeatureRule = array();
|
||||||
|
|
||||||
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
|
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
|
||||||
|
@ -148,10 +147,6 @@ class Solver
|
||||||
*/
|
*/
|
||||||
protected function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
|
protected function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
|
||||||
{
|
{
|
||||||
if (empty($packages)) {
|
|
||||||
return $this->createImpossibleRule($reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
$literals = array();
|
$literals = array();
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
$literals[] = new Literal($package, true);
|
$literals[] = new Literal($package, true);
|
||||||
|
@ -201,22 +196,6 @@ class Solver
|
||||||
return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
|
return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Intentionally creates a rule impossible to solve
|
|
||||||
*
|
|
||||||
* The rule is an empty one so it can never be satisfied.
|
|
||||||
*
|
|
||||||
* @param int $reason A RULE_* constant describing the reason for
|
|
||||||
* generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the package name, that goes with
|
|
||||||
* the reason
|
|
||||||
* @return Rule An empty rule
|
|
||||||
*/
|
|
||||||
protected function createImpossibleRule($reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
return new Rule(array(), $reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a rule unless it duplicates an existing one of any type
|
* Adds a rule unless it duplicates an existing one of any type
|
||||||
*
|
*
|
||||||
|
@ -305,7 +284,7 @@ class Solver
|
||||||
// if ignoreinstalledsobsoletes is not set, we're also checking
|
// if ignoreinstalledsobsoletes is not set, we're also checking
|
||||||
// obsoletes of installed packages (like newer rpm versions)
|
// obsoletes of installed packages (like newer rpm versions)
|
||||||
//
|
//
|
||||||
/** @TODO: if ($this->noInstalledObsoletes) */
|
/** TODO if ($this->noInstalledObsoletes) */
|
||||||
if (true) {
|
if (true) {
|
||||||
$noObsoletes = isset($this->noObsoletes[$package->getId()]);
|
$noObsoletes = isset($this->noObsoletes[$package->getId()]);
|
||||||
$isInstalled = (isset($this->installedMap[$package->getId()]));
|
$isInstalled = (isset($this->installedMap[$package->getId()]));
|
||||||
|
@ -508,7 +487,7 @@ class Solver
|
||||||
|
|
||||||
// push all of our rules (can only be feature or job rules)
|
// push all of our rules (can only be feature or job rules)
|
||||||
// asserting this literal on the problem stack
|
// asserting this literal on the problem stack
|
||||||
foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_UPDATE, RuleSet::TYPE_FEATURE)) as $assertRule) {
|
foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_FEATURE)) as $assertRule) {
|
||||||
if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
|
if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -882,11 +861,6 @@ class Solver
|
||||||
|
|
||||||
protected function disableUpdateRule($package)
|
protected function disableUpdateRule($package)
|
||||||
{
|
{
|
||||||
// find update & feature rule and disable
|
|
||||||
if (isset($this->packageToUpdateRule[$package->getId()])) {
|
|
||||||
$this->packageToUpdateRule[$package->getId()]->disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->packageToFeatureRule[$package->getId()])) {
|
if (isset($this->packageToFeatureRule[$package->getId()])) {
|
||||||
$this->packageToFeatureRule[$package->getId()]->disable();
|
$this->packageToFeatureRule[$package->getId()]->disable();
|
||||||
}
|
}
|
||||||
|
@ -958,6 +932,14 @@ class Solver
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch ($job['cmd']) {
|
||||||
|
case 'update-all':
|
||||||
|
foreach ($installedPackages as $package) {
|
||||||
|
$this->updateMap[$package->getId()] = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($installedPackages as $package) {
|
foreach ($installedPackages as $package) {
|
||||||
|
@ -986,22 +968,21 @@ class Solver
|
||||||
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
|
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
|
||||||
$rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
|
$rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
|
||||||
|
|
||||||
if ($this->policy->allowUninstall()) {
|
$rule->setWeak(true);
|
||||||
$rule->setWeak(true);
|
$this->addRule(RuleSet::TYPE_FEATURE, $rule);
|
||||||
$this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
|
$this->packageToFeatureRule[$package->getId()] = $rule;
|
||||||
$this->packageToFeatureRule[$package->getId()] = $rule;
|
|
||||||
} else {
|
|
||||||
$this->addRule(RuleSet::TYPE_UPDATE, $rule);
|
|
||||||
$this->packageToUpdateRule[$package->getId()] = $rule;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->jobs as $job) {
|
foreach ($this->jobs as $job) {
|
||||||
switch ($job['cmd']) {
|
switch ($job['cmd']) {
|
||||||
case 'install':
|
case 'install':
|
||||||
$rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
|
if (empty($job['packages'])) {
|
||||||
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
$this->problems[] = array($job);
|
||||||
$this->ruleToJob[$rule->getId()] = $job;
|
} else {
|
||||||
|
$rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
|
||||||
|
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
||||||
|
$this->ruleToJob[$rule->getId()] = $job;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'remove':
|
case 'remove':
|
||||||
// remove all packages with this name including uninstalled
|
// remove all packages with this name including uninstalled
|
||||||
|
@ -1046,6 +1027,10 @@ class Solver
|
||||||
//findrecommendedsuggested(solv);
|
//findrecommendedsuggested(solv);
|
||||||
//solver_prepare_solutions(solv);
|
//solver_prepare_solutions(solv);
|
||||||
|
|
||||||
|
if ($this->problems) {
|
||||||
|
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->createTransaction();
|
return $this->createTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,9 +1046,6 @@ class Solver
|
||||||
if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) {
|
if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) {
|
||||||
$literals = array();
|
$literals = array();
|
||||||
|
|
||||||
if (isset($this->packageToUpdateRule[$package->getId()])) {
|
|
||||||
$literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals());
|
|
||||||
}
|
|
||||||
if (isset($this->packageToFeatureRule[$package->getId()])) {
|
if (isset($this->packageToFeatureRule[$package->getId()])) {
|
||||||
$literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals());
|
$literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals());
|
||||||
}
|
}
|
||||||
|
@ -1127,6 +1109,8 @@ class Solver
|
||||||
|
|
||||||
protected function addDecision(Literal $l, $level)
|
protected function addDecision(Literal $l, $level)
|
||||||
{
|
{
|
||||||
|
assert($this->decisionMap[$l->getPackageId()] == 0);
|
||||||
|
|
||||||
if ($l->isWanted()) {
|
if ($l->isWanted()) {
|
||||||
$this->decisionMap[$l->getPackageId()] = $level;
|
$this->decisionMap[$l->getPackageId()] = $level;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1137,6 +1121,9 @@ class Solver
|
||||||
protected function addDecisionId($literalId, $level)
|
protected function addDecisionId($literalId, $level)
|
||||||
{
|
{
|
||||||
$packageId = abs($literalId);
|
$packageId = abs($literalId);
|
||||||
|
|
||||||
|
assert($this->decisionMap[$packageId] == 0);
|
||||||
|
|
||||||
if ($literalId > 0) {
|
if ($literalId > 0) {
|
||||||
$this->decisionMap[$packageId] = $level;
|
$this->decisionMap[$packageId] = $level;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1179,8 +1166,8 @@ class Solver
|
||||||
{
|
{
|
||||||
$packageId = abs($literalId);
|
$packageId = abs($literalId);
|
||||||
return (
|
return (
|
||||||
$this->decisionMap[$packageId] > 0 && !($literalId < 0) ||
|
($this->decisionMap[$packageId] > 0 && $literalId < 0) ||
|
||||||
$this->decisionMap[$packageId] < 0 && $literalId > 0
|
($this->decisionMap[$packageId] < 0 && $literalId > 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,7 +1214,8 @@ class Solver
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($rule = $this->watches[$literal->getId()]; $rule !== null; $rule = $nextRule) {
|
$prevRule = null;
|
||||||
|
for ($rule = $this->watches[$literal->getId()]; $rule !== null; $prevRule = $rule, $rule = $nextRule) {
|
||||||
$nextRule = $rule->getNext($literal);
|
$nextRule = $rule->getNext($literal);
|
||||||
|
|
||||||
if ($rule->isDisabled()) {
|
if ($rule->isDisabled()) {
|
||||||
|
@ -1247,16 +1235,27 @@ class Solver
|
||||||
if ($otherWatch !== $ruleLiteral->getId() &&
|
if ($otherWatch !== $ruleLiteral->getId() &&
|
||||||
!$this->decisionsConflict($ruleLiteral)) {
|
!$this->decisionsConflict($ruleLiteral)) {
|
||||||
|
|
||||||
|
|
||||||
if ($literal->getId() === $rule->watch1) {
|
if ($literal->getId() === $rule->watch1) {
|
||||||
$rule->watch1 = $ruleLiteral->getId();
|
$rule->watch1 = $ruleLiteral->getId();
|
||||||
$rule->next1 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
|
$rule->next1 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null;
|
||||||
} else {
|
} else {
|
||||||
$rule->watch2 = $ruleLiteral->getId();
|
$rule->watch2 = $ruleLiteral->getId();
|
||||||
$rule->next2 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
|
$rule->next2 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prevRule) {
|
||||||
|
if ($prevRule->next1 == $rule) {
|
||||||
|
$prevRule->next1 = $nextRule;
|
||||||
|
} else {
|
||||||
|
$prevRule->next2 = $nextRule;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->watches[$literal->getId()] = $nextRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->watches[$ruleLiteral->getId()] = $rule;
|
$this->watches[$ruleLiteral->getId()] = $rule;
|
||||||
|
|
||||||
|
$rule = $prevRule;
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1487,7 +1486,7 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
$why = count($this->learnedPool) - 1;
|
$why = count($this->learnedPool) - 1;
|
||||||
|
assert($learnedLiterals[0] !== null);
|
||||||
$newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why);
|
$newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why);
|
||||||
|
|
||||||
return array($ruleLevel, $newRule, $why);
|
return array($ruleLevel, $newRule, $why);
|
||||||
|
@ -1813,11 +1812,7 @@ class Solver
|
||||||
|
|
||||||
$rule = null;
|
$rule = null;
|
||||||
|
|
||||||
if (isset($this->packageToUpdateRule[$literal->getPackageId()])) {
|
if (isset($this->packageToFeatureRule[$literal->getPackageId()])) {
|
||||||
$rule = $this->packageToUpdateRule[$literal->getPackageId()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!$rule || $rule->isDisabled()) && isset($this->packageToFeatureRule[$literal->getPackageId()])) {
|
|
||||||
$rule = $this->packageToFeatureRule[$literal->getPackageId()];
|
$rule = $this->packageToFeatureRule[$literal->getPackageId()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2027,8 +2022,10 @@ class Solver
|
||||||
}
|
}
|
||||||
if ($level > 0) {
|
if ($level > 0) {
|
||||||
echo ' +' . $this->pool->packageById($packageId)."\n";
|
echo ' +' . $this->pool->packageById($packageId)."\n";
|
||||||
} else {
|
} elseif ($level < 0) {
|
||||||
echo ' -' . $this->pool->packageById($packageId)."\n";
|
echo ' -' . $this->pool->packageById($packageId)."\n";
|
||||||
|
} else {
|
||||||
|
echo ' ?' . $this->pool->packageById($packageId)."\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "\n";
|
echo "\n";
|
||||||
|
@ -2042,4 +2039,41 @@ class Solver
|
||||||
}
|
}
|
||||||
echo "\n";
|
echo "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?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 SolverProblemsException extends \RuntimeException
|
||||||
|
{
|
||||||
|
protected $problems;
|
||||||
|
|
||||||
|
public function __construct(array $problems, array $learnedPool)
|
||||||
|
{
|
||||||
|
$message = '';
|
||||||
|
foreach ($problems as $i => $problem) {
|
||||||
|
$message .= '[';
|
||||||
|
foreach ($problem as $why) {
|
||||||
|
|
||||||
|
if (is_int($why) && isset($learnedPool[$why])) {
|
||||||
|
$rules = $learnedPool[$why];
|
||||||
|
} else {
|
||||||
|
$rules = $why;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($rules['packages'])) {
|
||||||
|
$message .= $this->jobToText($rules);
|
||||||
|
} else {
|
||||||
|
$message .= '(';
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
if ($rule instanceof Rule) {
|
||||||
|
if ($rule->getType() == RuleSet::TYPE_LEARNED) {
|
||||||
|
$message .= 'learned: ';
|
||||||
|
}
|
||||||
|
$message .= $rule . ', ';
|
||||||
|
} else {
|
||||||
|
$message .= 'String(' . $rule . '), ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$message .= ')';
|
||||||
|
}
|
||||||
|
$message .= ', ';
|
||||||
|
}
|
||||||
|
$message .= "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jobToText($job)
|
||||||
|
{
|
||||||
|
//$output = serialize($job);
|
||||||
|
$output = 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.implode(', ', $job['packages']).'])';
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,14 +125,14 @@ class DownloadManager
|
||||||
$sourceType = $package->getSourceType();
|
$sourceType = $package->getSourceType();
|
||||||
$distType = $package->getDistType();
|
$distType = $package->getDistType();
|
||||||
|
|
||||||
if (!($preferSource && $sourceType) && $distType) {
|
if (!$package->isDev() && !($preferSource && $sourceType) && $distType) {
|
||||||
$package->setInstallationSource('dist');
|
$package->setInstallationSource('dist');
|
||||||
} elseif ($sourceType) {
|
} elseif ($sourceType) {
|
||||||
$package->setInstallationSource('source');
|
$package->setInstallationSource('source');
|
||||||
|
} elseif ($package->isDev()) {
|
||||||
|
throw new \InvalidArgumentException('Dev package '.$package.' must have a source specified');
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
|
||||||
'Package '.$package.' should have source or dist specified'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$fs = new Filesystem();
|
$fs = new Filesystem();
|
||||||
|
|
|
@ -71,7 +71,15 @@ class Factory
|
||||||
$rm = $this->createRepositoryManager($io);
|
$rm = $this->createRepositoryManager($io);
|
||||||
|
|
||||||
// load default repository unless it's explicitly disabled
|
// load default repository unless it's explicitly disabled
|
||||||
if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) {
|
$loadPackagist = true;
|
||||||
|
if (isset($packageConfig['repositories'])) {
|
||||||
|
foreach ($packageConfig['repositories'] as $repo) {
|
||||||
|
if (isset($repo['packagist']) && $repo['packagist'] === false) {
|
||||||
|
$loadPackagist = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($loadPackagist) {
|
||||||
$this->addPackagistRepository($rm);
|
$this->addPackagistRepository($rm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,9 @@ class LibraryInstaller implements InstallerInterface
|
||||||
*/
|
*/
|
||||||
public function install(PackageInterface $package)
|
public function install(PackageInterface $package)
|
||||||
{
|
{
|
||||||
|
$this->initializeDirs();
|
||||||
$downloadPath = $this->getInstallPath($package);
|
$downloadPath = $this->getInstallPath($package);
|
||||||
|
|
||||||
$this->filesystem->ensureDirectoryExists($this->vendorDir);
|
|
||||||
$this->filesystem->ensureDirectoryExists($this->binDir);
|
|
||||||
|
|
||||||
// remove the binaries if it appears the package files are missing
|
// remove the binaries if it appears the package files are missing
|
||||||
if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) {
|
if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) {
|
||||||
$this->removeBinaries($package);
|
$this->removeBinaries($package);
|
||||||
|
@ -104,16 +102,16 @@ class LibraryInstaller implements InstallerInterface
|
||||||
throw new \InvalidArgumentException('Package is not installed: '.$initial);
|
throw new \InvalidArgumentException('Package is not installed: '.$initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->initializeDirs();
|
||||||
$downloadPath = $this->getInstallPath($initial);
|
$downloadPath = $this->getInstallPath($initial);
|
||||||
|
|
||||||
$this->filesystem->ensureDirectoryExists($this->vendorDir);
|
|
||||||
$this->filesystem->ensureDirectoryExists($this->binDir);
|
|
||||||
|
|
||||||
$this->removeBinaries($initial);
|
$this->removeBinaries($initial);
|
||||||
$this->downloadManager->update($initial, $target, $downloadPath);
|
$this->downloadManager->update($initial, $target, $downloadPath);
|
||||||
$this->installBinaries($target);
|
$this->installBinaries($target);
|
||||||
$this->repository->removePackage($initial);
|
$this->repository->removePackage($initial);
|
||||||
$this->repository->addPackage(clone $target);
|
if (!$this->repository->hasPackage($target)) {
|
||||||
|
$this->repository->addPackage(clone $target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,6 +189,14 @@ class LibraryInstaller implements InstallerInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function initializeDirs()
|
||||||
|
{
|
||||||
|
$this->filesystem->ensureDirectoryExists($this->vendorDir);
|
||||||
|
$this->filesystem->ensureDirectoryExists($this->binDir);
|
||||||
|
$this->vendorDir = realpath($this->vendorDir);
|
||||||
|
$this->binDir = realpath($this->binDir);
|
||||||
|
}
|
||||||
|
|
||||||
private function generateWindowsProxyCode($bin, $link)
|
private function generateWindowsProxyCode($bin, $link)
|
||||||
{
|
{
|
||||||
$binPath = $this->filesystem->findShortestPath($link, $bin);
|
$binPath = $this->filesystem->findShortestPath($link, $bin);
|
||||||
|
|
|
@ -122,6 +122,8 @@ class ArrayLoader
|
||||||
$package->setSourceType($config['source']['type']);
|
$package->setSourceType($config['source']['type']);
|
||||||
$package->setSourceUrl($config['source']['url']);
|
$package->setSourceUrl($config['source']['url']);
|
||||||
$package->setSourceReference($config['source']['reference']);
|
$package->setSourceReference($config['source']['reference']);
|
||||||
|
} elseif ($package->isDev()) {
|
||||||
|
throw new \UnexpectedValueException('Dev package '.$package.' must have a source specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($config['dist'])) {
|
if (isset($config['dist'])) {
|
||||||
|
|
|
@ -38,20 +38,23 @@ class RootPackageLoader extends ArrayLoader
|
||||||
$config['name'] = '__root__';
|
$config['name'] = '__root__';
|
||||||
}
|
}
|
||||||
if (!isset($config['version'])) {
|
if (!isset($config['version'])) {
|
||||||
$config['version'] = '1.0.0-dev';
|
$config['version'] = '1.0.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
$package = parent::load($config);
|
$package = parent::load($config);
|
||||||
|
|
||||||
if (isset($config['repositories'])) {
|
if (isset($config['repositories'])) {
|
||||||
foreach ($config['repositories'] as $repoName => $repo) {
|
foreach ($config['repositories'] as $index => $repo) {
|
||||||
if (false === $repo && 'packagist' === $repoName) {
|
if (isset($repo['packagist']) && $repo['packagist'] === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!is_array($repo)) {
|
if (!is_array($repo)) {
|
||||||
throw new \UnexpectedValueException('Repository '.$repoName.' in '.$package->getPrettyName().' '.$package->getVersion().' should be an array, '.gettype($repo).' given');
|
throw new \UnexpectedValueException('Repository '.$index.' should be an array, '.gettype($repo).' given');
|
||||||
}
|
}
|
||||||
$repository = $this->manager->createRepository(key($repo), current($repo));
|
if (!isset($repo['type'])) {
|
||||||
|
throw new \UnexpectedValueException('Repository '.$index.' must have a type defined');
|
||||||
|
}
|
||||||
|
$repository = $this->manager->createRepository($repo['type'], $repo);
|
||||||
$this->manager->addRepository($repository);
|
$this->manager->addRepository($repository);
|
||||||
}
|
}
|
||||||
$package->setRepositories($config['repositories']);
|
$package->setRepositories($config['repositories']);
|
||||||
|
|
|
@ -69,11 +69,7 @@ class Locker
|
||||||
*/
|
*/
|
||||||
public function getLockedPackages()
|
public function getLockedPackages()
|
||||||
{
|
{
|
||||||
if (!$this->isLocked()) {
|
$lockList = $this->getLockData();
|
||||||
throw new \LogicException('No lockfile found. Unable to read locked packages');
|
|
||||||
}
|
|
||||||
|
|
||||||
$lockList = $this->lockFile->read();
|
|
||||||
$packages = array();
|
$packages = array();
|
||||||
foreach ($lockList['packages'] as $info) {
|
foreach ($lockList['packages'] as $info) {
|
||||||
$package = $this->repositoryManager->getLocalRepository()->findPackage($info['package'], $info['version']);
|
$package = $this->repositoryManager->getLocalRepository()->findPackage($info['package'], $info['version']);
|
||||||
|
@ -95,6 +91,15 @@ class Locker
|
||||||
return $packages;
|
return $packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLockData()
|
||||||
|
{
|
||||||
|
if (!$this->isLocked()) {
|
||||||
|
throw new \LogicException('No lockfile found. Unable to read locked packages');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->lockFile->read();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locks provided packages into lockfile.
|
* Locks provided packages into lockfile.
|
||||||
*
|
*
|
||||||
|
@ -116,8 +121,17 @@ class Locker
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$lock['packages'][] = array('package' => $name, 'version' => $version);
|
$spec = array('package' => $name, 'version' => $version);
|
||||||
|
|
||||||
|
if ($package->isDev()) {
|
||||||
|
$spec['source-reference'] = $package->getSourceReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
$lock['packages'][] = $spec;
|
||||||
}
|
}
|
||||||
|
usort($lock['packages'], function ($a, $b) {
|
||||||
|
return strcmp($a['package'], $b['package']);
|
||||||
|
});
|
||||||
|
|
||||||
$this->lockFile->write($lock);
|
$this->lockFile->write($lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ class MemoryPackage extends BasePackage
|
||||||
protected $extra = array();
|
protected $extra = array();
|
||||||
protected $binaries = array();
|
protected $binaries = array();
|
||||||
protected $scripts = array();
|
protected $scripts = array();
|
||||||
|
protected $dev;
|
||||||
|
|
||||||
protected $requires = array();
|
protected $requires = array();
|
||||||
protected $conflicts = array();
|
protected $conflicts = array();
|
||||||
|
@ -63,6 +64,16 @@ class MemoryPackage extends BasePackage
|
||||||
|
|
||||||
$this->version = $version;
|
$this->version = $version;
|
||||||
$this->prettyVersion = $prettyVersion;
|
$this->prettyVersion = $prettyVersion;
|
||||||
|
|
||||||
|
$this->dev = 'dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function isDev()
|
||||||
|
{
|
||||||
|
return $this->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -68,6 +68,13 @@ interface PackageInterface
|
||||||
*/
|
*/
|
||||||
function matches($name, LinkConstraintInterface $constraint);
|
function matches($name, LinkConstraintInterface $constraint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the package is a development virtual package or a concrete one
|
||||||
|
*
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isDev();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the package type, e.g. library
|
* Returns the package type, e.g. library
|
||||||
*
|
*
|
||||||
|
|
|
@ -34,10 +34,15 @@ class VersionParser
|
||||||
{
|
{
|
||||||
$version = trim($version);
|
$version = trim($version);
|
||||||
|
|
||||||
if (preg_match('{^(?:master|trunk|default)(?:[.-]?dev)?$}i', $version)) {
|
// match master-like branches
|
||||||
|
if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
|
||||||
return '9999999-dev';
|
return '9999999-dev';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('dev-' === strtolower(substr($version, 0, 4))) {
|
||||||
|
return strtolower($version);
|
||||||
|
}
|
||||||
|
|
||||||
// match classical versioning
|
// match classical versioning
|
||||||
if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.$this->modifierRegex.'$}i', $version, $matches)) {
|
if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.$this->modifierRegex.'$}i', $version, $matches)) {
|
||||||
$version = $matches[1]
|
$version = $matches[1]
|
||||||
|
@ -53,7 +58,7 @@ class VersionParser
|
||||||
// add version modifiers if a version was matched
|
// add version modifiers if a version was matched
|
||||||
if (isset($index)) {
|
if (isset($index)) {
|
||||||
if (!empty($matches[$index])) {
|
if (!empty($matches[$index])) {
|
||||||
$mod = array('{^pl?$}', '{^rc$}');
|
$mod = array('{^pl?$}i', '{^rc$}i');
|
||||||
$modNormalized = array('patch', 'RC');
|
$modNormalized = array('patch', 'RC');
|
||||||
$version .= '-'.preg_replace($mod, $modNormalized, strtolower($matches[$index]))
|
$version .= '-'.preg_replace($mod, $modNormalized, strtolower($matches[$index]))
|
||||||
. (!empty($matches[$index+1]) ? $matches[$index+1] : '');
|
. (!empty($matches[$index+1]) ? $matches[$index+1] : '');
|
||||||
|
@ -97,7 +102,7 @@ class VersionParser
|
||||||
return str_replace('x', '9999999', $version).'-dev';
|
return str_replace('x', '9999999', $version).'-dev';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \UnexpectedValueException('Invalid branch name '.$name);
|
return 'dev-'.$name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,7 +33,7 @@ class PackageRepository extends ArrayRepository
|
||||||
*/
|
*/
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
{
|
{
|
||||||
$this->config = $config;
|
$this->config = $config['package'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,6 +22,7 @@ use Composer\Util\StreamContextFactory;
|
||||||
class PearRepository extends ArrayRepository
|
class PearRepository extends ArrayRepository
|
||||||
{
|
{
|
||||||
private $url;
|
private $url;
|
||||||
|
private $channel;
|
||||||
private $streamContext;
|
private $streamContext;
|
||||||
|
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
|
@ -29,11 +30,14 @@ class PearRepository extends ArrayRepository
|
||||||
if (!preg_match('{^https?://}', $config['url'])) {
|
if (!preg_match('{^https?://}', $config['url'])) {
|
||||||
$config['url'] = 'http://'.$config['url'];
|
$config['url'] = 'http://'.$config['url'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filter_var($config['url'], FILTER_VALIDATE_URL)) {
|
if (!filter_var($config['url'], FILTER_VALIDATE_URL)) {
|
||||||
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']);
|
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->url = rtrim($config['url'], '/');
|
$this->url = rtrim($config['url'], '/');
|
||||||
|
|
||||||
|
$this->channel = !empty($config['channel']) ? $config['channel'] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initialize()
|
protected function initialize()
|
||||||
|
@ -50,6 +54,12 @@ class PearRepository extends ArrayRepository
|
||||||
|
|
||||||
protected function fetchFromServer()
|
protected function fetchFromServer()
|
||||||
{
|
{
|
||||||
|
if (!$this->channel) {
|
||||||
|
$channelXML = $this->requestXml($this->url . "/channel.xml");
|
||||||
|
$this->channel = $channelXML->getElementsByTagName("suggestedalias")->item(0)->nodeValue
|
||||||
|
?: $channelXML->getElementsByTagName("name")->item(0)->nodeValue;
|
||||||
|
}
|
||||||
|
|
||||||
$categoryXML = $this->requestXml($this->url . "/rest/c/categories.xml");
|
$categoryXML = $this->requestXml($this->url . "/rest/c/categories.xml");
|
||||||
$categories = $categoryXML->getElementsByTagName("c");
|
$categories = $categoryXML->getElementsByTagName("c");
|
||||||
|
|
||||||
|
@ -81,6 +91,7 @@ class PearRepository extends ArrayRepository
|
||||||
$loader = new ArrayLoader();
|
$loader = new ArrayLoader();
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
$packageName = $package->nodeValue;
|
$packageName = $package->nodeValue;
|
||||||
|
$fullName = 'pear-'.$this->channel.'/'.$packageName;
|
||||||
|
|
||||||
$packageLink = $package->getAttribute('xlink:href');
|
$packageLink = $package->getAttribute('xlink:href');
|
||||||
$releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink);
|
$releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink);
|
||||||
|
@ -102,7 +113,7 @@ class PearRepository extends ArrayRepository
|
||||||
$pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
|
$pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
|
||||||
|
|
||||||
$packageData = array(
|
$packageData = array(
|
||||||
'name' => $packageName,
|
'name' => $fullName,
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"),
|
'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"),
|
||||||
'version' => $pearVersion,
|
'version' => $pearVersion,
|
||||||
|
@ -220,8 +231,9 @@ class PearRepository extends ArrayRepository
|
||||||
$package = $information->getElementsByTagName('p')->item(0);
|
$package = $information->getElementsByTagName('p')->item(0);
|
||||||
|
|
||||||
$packageName = $package->getElementsByTagName('n')->item(0)->nodeValue;
|
$packageName = $package->getElementsByTagName('n')->item(0)->nodeValue;
|
||||||
|
$fullName = 'pear-'.$this->channel.'/'.$packageName;
|
||||||
$packageData = array(
|
$packageData = array(
|
||||||
'name' => $packageName,
|
'name' => $fullName,
|
||||||
'type' => 'library'
|
'type' => 'library'
|
||||||
);
|
);
|
||||||
$packageKeys = array('l' => 'license', 'd' => 'description');
|
$packageKeys = array('l' => 'license', 'd' => 'description');
|
||||||
|
|
|
@ -15,6 +15,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
|
||||||
protected $branches;
|
protected $branches;
|
||||||
protected $rootIdentifier;
|
protected $rootIdentifier;
|
||||||
protected $infoCache = array();
|
protected $infoCache = array();
|
||||||
|
protected $isLocal = false;
|
||||||
|
|
||||||
public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
|
public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
|
||||||
{
|
{
|
||||||
|
@ -30,10 +31,15 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
|
||||||
{
|
{
|
||||||
$url = escapeshellarg($this->url);
|
$url = escapeshellarg($this->url);
|
||||||
$tmpDir = escapeshellarg($this->tmpDir);
|
$tmpDir = escapeshellarg($this->tmpDir);
|
||||||
if (is_dir($this->tmpDir)) {
|
|
||||||
$this->process->execute(sprintf('cd %s && git fetch origin', $tmpDir), $output);
|
if (static::isLocalUrl($url)) {
|
||||||
|
$this->isLocal = true;
|
||||||
} else {
|
} else {
|
||||||
$this->process->execute(sprintf('git clone %s %s', $url, $tmpDir), $output);
|
if (is_dir($this->tmpDir)) {
|
||||||
|
$this->process->execute(sprintf('cd %s && git fetch origin', $tmpDir), $output);
|
||||||
|
} else {
|
||||||
|
$this->process->execute(sprintf('git clone %s %s', $url, $tmpDir), $output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getTags();
|
$this->getTags();
|
||||||
|
@ -47,11 +53,27 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
|
||||||
{
|
{
|
||||||
if (null === $this->rootIdentifier) {
|
if (null === $this->rootIdentifier) {
|
||||||
$this->rootIdentifier = 'master';
|
$this->rootIdentifier = 'master';
|
||||||
$this->process->execute(sprintf('cd %s && git branch --no-color -r', escapeshellarg($this->tmpDir)), $output);
|
|
||||||
foreach ($this->process->splitLines($output) as $branch) {
|
if ($this->isLocal) {
|
||||||
if ($branch && preg_match('{/HEAD +-> +[^/]+/(\S+)}', $branch, $match)) {
|
// select currently checked out branch if master is not available
|
||||||
$this->rootIdentifier = $match[1];
|
$this->process->execute(sprintf('cd %s && git branch --no-color', escapeshellarg($this->tmpDir)), $output);
|
||||||
break;
|
$branches = $this->process->splitLines($output);
|
||||||
|
if (!in_array('* master', $branches)) {
|
||||||
|
foreach ($branches as $branch) {
|
||||||
|
if ($branch && preg_match('{^\* +(\S+)}', $branch, $match)) {
|
||||||
|
$this->rootIdentifier = $match[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// try to find a non-master remote HEAD branch
|
||||||
|
$this->process->execute(sprintf('cd %s && git branch --no-color -r', escapeshellarg($this->tmpDir)), $output);
|
||||||
|
foreach ($this->process->splitLines($output) as $branch) {
|
||||||
|
if ($branch && preg_match('{/HEAD +-> +[^/]+/(\S+)}', $branch, $match)) {
|
||||||
|
$this->rootIdentifier = $match[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +154,11 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
|
||||||
if (null === $this->branches) {
|
if (null === $this->branches) {
|
||||||
$branches = array();
|
$branches = array();
|
||||||
|
|
||||||
$this->process->execute(sprintf('cd %s && git branch --no-color -rv', escapeshellarg($this->tmpDir)), $output);
|
$this->process->execute(sprintf(
|
||||||
|
'cd %s && git branch --no-color --no-abbrev -v %s',
|
||||||
|
escapeshellarg($this->tmpDir),
|
||||||
|
$this->isLocal ? '' : '-r'
|
||||||
|
), $output);
|
||||||
foreach ($this->process->splitLines($output) as $branch) {
|
foreach ($this->process->splitLines($output) as $branch) {
|
||||||
if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
|
if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
|
||||||
preg_match('{^ *[^/]+/(\S+) *([a-f0-9]+) .*$}', $branch, $match);
|
preg_match('{^ *[^/]+/(\S+) *([a-f0-9]+) .*$}', $branch, $match);
|
||||||
|
@ -170,7 +196,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// local filesystem
|
// local filesystem
|
||||||
if (preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url)) {
|
if (static::isLocalUrl($url)) {
|
||||||
$process = new ProcessExecutor();
|
$process = new ProcessExecutor();
|
||||||
// check whether there is a git repo in that path
|
// check whether there is a git repo in that path
|
||||||
if ($process->execute(sprintf('cd %s && git show', escapeshellarg($url)), $output) === 0) {
|
if ($process->execute(sprintf('cd %s && git show', escapeshellarg($url)), $output) === 0) {
|
||||||
|
|
|
@ -68,4 +68,9 @@ abstract class VcsDriver
|
||||||
$rfs = new RemoteFilesystem($this->io);
|
$rfs = new RemoteFilesystem($this->io);
|
||||||
return $rfs->getContents($this->url, $url, false);
|
return $rfs->getContents($this->url, $url, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static function isLocalUrl($url)
|
||||||
|
{
|
||||||
|
return (Boolean) preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,20 +76,22 @@ class VcsRepository extends ArrayRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($driver->getTags() as $tag => $identifier) {
|
foreach ($driver->getTags() as $tag => $identifier) {
|
||||||
$this->io->overwrite('Get composer of <info>' . $this->packageName . '</info> (<comment>' . $tag . '</comment>)', false);
|
$msg = 'Get composer info for <info>' . $this->packageName . '</info> (<comment>' . $tag . '</comment>)';
|
||||||
|
if ($debug) {
|
||||||
|
$this->io->write($msg);
|
||||||
|
} else {
|
||||||
|
$this->io->overwrite($msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
$parsedTag = $this->validateTag($versionParser, $tag);
|
$parsedTag = $this->validateTag($versionParser, $tag);
|
||||||
if ($parsedTag && $driver->hasComposerFile($identifier)) {
|
if ($parsedTag && $driver->hasComposerFile($identifier)) {
|
||||||
try {
|
try {
|
||||||
$data = $driver->getComposerInformation($identifier);
|
$data = $driver->getComposerInformation($identifier);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if (strpos($e->getMessage(), 'JSON Parse Error') !== false) {
|
if ($debug) {
|
||||||
if ($debug) {
|
$this->io->write('Skipped tag '.$tag.', '.$e->getMessage());
|
||||||
$this->io->write('Skipped tag '.$tag.', '.$e->getMessage());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
throw $e;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// manually versioned package
|
// manually versioned package
|
||||||
|
@ -103,7 +105,7 @@ class VcsRepository extends ArrayRepository
|
||||||
|
|
||||||
// make sure tag packages have no -dev flag
|
// make sure tag packages have no -dev flag
|
||||||
$data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']);
|
$data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']);
|
||||||
$data['version_normalized'] = preg_replace('{[.-]?dev$}i', '', $data['version_normalized']);
|
$data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']);
|
||||||
|
|
||||||
// broken package, version doesn't match tag
|
// broken package, version doesn't match tag
|
||||||
if ($data['version_normalized'] !== $parsedTag) {
|
if ($data['version_normalized'] !== $parsedTag) {
|
||||||
|
@ -126,39 +128,33 @@ class VcsRepository extends ArrayRepository
|
||||||
$this->io->overwrite('', false);
|
$this->io->overwrite('', false);
|
||||||
|
|
||||||
foreach ($driver->getBranches() as $branch => $identifier) {
|
foreach ($driver->getBranches() as $branch => $identifier) {
|
||||||
$this->io->overwrite('Get composer of <info>' . $this->packageName . '</info> (<comment>' . $branch . '</comment>)', false);
|
$msg = 'Get composer info for <info>' . $this->packageName . '</info> (<comment>' . $branch . '</comment>)';
|
||||||
|
if ($debug) {
|
||||||
|
$this->io->write($msg);
|
||||||
|
} else {
|
||||||
|
$this->io->overwrite($msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
$parsedBranch = $this->validateBranch($versionParser, $branch);
|
$parsedBranch = $this->validateBranch($versionParser, $branch);
|
||||||
if ($driver->hasComposerFile($identifier)) {
|
if ($driver->hasComposerFile($identifier)) {
|
||||||
$data = $driver->getComposerInformation($identifier);
|
$data = $driver->getComposerInformation($identifier);
|
||||||
|
|
||||||
// manually versioned package
|
if (!$parsedBranch) {
|
||||||
if (isset($data['version'])) {
|
|
||||||
$data['version_normalized'] = $versionParser->normalize($data['version']);
|
|
||||||
} elseif ($parsedBranch) {
|
|
||||||
// auto-versionned package, read value from branch name
|
|
||||||
$data['version'] = $branch;
|
|
||||||
$data['version_normalized'] = $parsedBranch;
|
|
||||||
} else {
|
|
||||||
if ($debug) {
|
if ($debug) {
|
||||||
$this->io->write('Skipped branch '.$branch.', invalid name and no composer file was found');
|
$this->io->write('Skipped branch '.$branch.', invalid name and no composer file was found');
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure branch packages have a -dev flag
|
// branches are always auto-versionned, read value from branch name
|
||||||
$normalizedStableVersion = preg_replace('{[.-]?dev$}i', '', $data['version_normalized']);
|
$data['version'] = $branch;
|
||||||
$data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']) . '-dev';
|
$data['version_normalized'] = $parsedBranch;
|
||||||
$data['version_normalized'] = $normalizedStableVersion . '-dev';
|
|
||||||
|
|
||||||
// Skip branches that contain a version that has been tagged already
|
// make sure branch packages have a dev flag
|
||||||
foreach ($this->getPackages() as $package) {
|
if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) {
|
||||||
if ($normalizedStableVersion === $package->getVersion()) {
|
$data['version'] = 'dev-' . $data['version'];
|
||||||
if ($debug) {
|
} else {
|
||||||
$this->io->write('Skipped branch '.$branch.', already tagged');
|
$data['version'] = $data['version'] . '-dev';
|
||||||
}
|
|
||||||
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($debug) {
|
if ($debug) {
|
||||||
|
|
|
@ -46,4 +46,16 @@ class RequestTest extends TestCase
|
||||||
),
|
),
|
||||||
$request->getJobs());
|
$request->getJobs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUpdateAll()
|
||||||
|
{
|
||||||
|
$pool = new Pool;
|
||||||
|
$request = new Request($pool);
|
||||||
|
|
||||||
|
$request->updateAll();
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('cmd' => 'update-all', 'packages' => array())),
|
||||||
|
$request->getJobs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
|
||||||
new Rule(array(), 'job1', null),
|
new Rule(array(), 'job1', null),
|
||||||
new Rule(array(), 'job2', null),
|
new Rule(array(), 'job2', null),
|
||||||
),
|
),
|
||||||
RuleSet::TYPE_UPDATE => array(
|
RuleSet::TYPE_FEATURE => array(
|
||||||
new Rule(array(), 'update1', null),
|
new Rule(array(), 'update1', null),
|
||||||
),
|
),
|
||||||
RuleSet::TYPE_PACKAGE => array(),
|
RuleSet::TYPE_PACKAGE => array(),
|
||||||
|
@ -46,7 +46,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
|
||||||
$expected = array(
|
$expected = array(
|
||||||
$this->rules[RuleSet::TYPE_JOB][0],
|
$this->rules[RuleSet::TYPE_JOB][0],
|
||||||
$this->rules[RuleSet::TYPE_JOB][1],
|
$this->rules[RuleSet::TYPE_JOB][1],
|
||||||
$this->rules[RuleSet::TYPE_UPDATE][0],
|
$this->rules[RuleSet::TYPE_FEATURE][0],
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
|
@ -64,7 +64,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
|
||||||
$expected = array(
|
$expected = array(
|
||||||
RuleSet::TYPE_JOB,
|
RuleSet::TYPE_JOB,
|
||||||
RuleSet::TYPE_JOB,
|
RuleSet::TYPE_JOB,
|
||||||
RuleSet::TYPE_UPDATE,
|
RuleSet::TYPE_FEATURE,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
|
|
|
@ -27,10 +27,9 @@ class RuleSetTest extends TestCase
|
||||||
new Rule(array(), 'job1', null),
|
new Rule(array(), 'job1', null),
|
||||||
new Rule(array(), 'job2', null),
|
new Rule(array(), 'job2', null),
|
||||||
),
|
),
|
||||||
RuleSet::TYPE_UPDATE => array(
|
RuleSet::TYPE_FEATURE => array(
|
||||||
new Rule(array(), 'update1', null),
|
new Rule(array(), 'update1', null),
|
||||||
),
|
),
|
||||||
RuleSet::TYPE_FEATURE => array(),
|
|
||||||
RuleSet::TYPE_LEARNED => array(),
|
RuleSet::TYPE_LEARNED => array(),
|
||||||
RuleSet::TYPE_CHOICE => array(),
|
RuleSet::TYPE_CHOICE => array(),
|
||||||
);
|
);
|
||||||
|
@ -38,7 +37,7 @@ class RuleSetTest extends TestCase
|
||||||
$ruleSet = new RuleSet;
|
$ruleSet = new RuleSet;
|
||||||
|
|
||||||
$ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB);
|
$ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB);
|
||||||
$ruleSet->add($rules[RuleSet::TYPE_UPDATE][0], RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rules[RuleSet::TYPE_FEATURE][0], RuleSet::TYPE_FEATURE);
|
||||||
$ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB);
|
$ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB);
|
||||||
|
|
||||||
$this->assertEquals($rules, $ruleSet->getRules());
|
$this->assertEquals($rules, $ruleSet->getRules());
|
||||||
|
@ -81,7 +80,7 @@ class RuleSetTest extends TestCase
|
||||||
$rule1 = new Rule(array(), 'job1', null);
|
$rule1 = new Rule(array(), 'job1', null);
|
||||||
$rule2 = new Rule(array(), 'job1', null);
|
$rule2 = new Rule(array(), 'job1', null);
|
||||||
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
||||||
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$iterator = $ruleSet->getIterator();
|
$iterator = $ruleSet->getIterator();
|
||||||
|
|
||||||
|
@ -97,9 +96,9 @@ class RuleSetTest extends TestCase
|
||||||
$rule2 = new Rule(array(), 'job1', null);
|
$rule2 = new Rule(array(), 'job1', null);
|
||||||
|
|
||||||
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
||||||
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_UPDATE);
|
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$this->assertSame($rule2, $iterator->current());
|
$this->assertSame($rule2, $iterator->current());
|
||||||
}
|
}
|
||||||
|
@ -111,7 +110,7 @@ class RuleSetTest extends TestCase
|
||||||
$rule2 = new Rule(array(), 'job1', null);
|
$rule2 = new Rule(array(), 'job1', null);
|
||||||
|
|
||||||
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
|
||||||
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB);
|
$iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB);
|
||||||
|
|
||||||
|
@ -143,7 +142,7 @@ class RuleSetTest extends TestCase
|
||||||
->method('equal')
|
->method('equal')
|
||||||
->will($this->returnValue(false));
|
->will($this->returnValue(false));
|
||||||
|
|
||||||
$ruleSet->add($rule, RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rule, RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$this->assertTrue($ruleSet->containsEqual($rule));
|
$this->assertTrue($ruleSet->containsEqual($rule));
|
||||||
$this->assertFalse($ruleSet->containsEqual($rule2));
|
$this->assertFalse($ruleSet->containsEqual($rule2));
|
||||||
|
@ -156,9 +155,9 @@ class RuleSetTest extends TestCase
|
||||||
$literal = new Literal($this->getPackage('foo', '2.1'), true);
|
$literal = new Literal($this->getPackage('foo', '2.1'), true);
|
||||||
$rule = new Rule(array($literal), 'job1', null);
|
$rule = new Rule(array($literal), 'job1', null);
|
||||||
|
|
||||||
$ruleSet->add($rule, RuleSet::TYPE_UPDATE);
|
$ruleSet->add($rule, RuleSet::TYPE_FEATURE);
|
||||||
|
|
||||||
$this->assertContains('UPDATE : (+foo-2.1.0.0)', $ruleSet->__toString());
|
$this->assertContains('FEATURE : (+foo-2.1.0.0)', $ruleSet->__toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRuleMock()
|
private function getRuleMock()
|
||||||
|
|
|
@ -19,6 +19,7 @@ use Composer\DependencyResolver\DefaultPolicy;
|
||||||
use Composer\DependencyResolver\Pool;
|
use Composer\DependencyResolver\Pool;
|
||||||
use Composer\DependencyResolver\Request;
|
use Composer\DependencyResolver\Request;
|
||||||
use Composer\DependencyResolver\Solver;
|
use Composer\DependencyResolver\Solver;
|
||||||
|
use Composer\DependencyResolver\SolverProblemsException;
|
||||||
use Composer\Package\Link;
|
use Composer\Package\Link;
|
||||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||||
use Composer\Test\TestCase;
|
use Composer\Test\TestCase;
|
||||||
|
@ -54,13 +55,28 @@ class SolverTest extends TestCase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testInstallNonExistingPackageFails()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($this->getPackage('A', '1.0'));
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('B');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$transaction = $this->solver->solve($this->request);
|
||||||
|
$this->fail('Unsolvable conflict did not resolve in exception.');
|
||||||
|
} catch (SolverProblemsException $e) {
|
||||||
|
// TODO assert problem properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testSolverInstallWithDeps()
|
public function testSolverInstallWithDeps()
|
||||||
{
|
{
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
||||||
|
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('<', '1.1'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -122,12 +138,12 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0.0.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0.0.0'), 'requires')));
|
||||||
|
|
||||||
$this->request->install('A', new VersionConstraint('=', '1.0.0.0'));
|
$this->request->install('A', $this->getVersionConstraint('=', '1.0.0.0'));
|
||||||
$this->request->install('B', new VersionConstraint('=', '1.1.0.0'));
|
$this->request->install('B', $this->getVersionConstraint('=', '1.1.0.0'));
|
||||||
$this->request->update('A', new VersionConstraint('=', '1.0.0.0'));
|
$this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
|
||||||
$this->request->update('B', new VersionConstraint('=', '1.0.0.0'));
|
$this->request->update('B', $this->getVersionConstraint('=', '1.0.0.0'));
|
||||||
|
|
||||||
$this->checkSolverResult(array(
|
$this->checkSolverResult(array(
|
||||||
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
|
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
|
||||||
|
@ -147,6 +163,26 @@ class SolverTest extends TestCase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSolverUpdateAll()
|
||||||
|
{
|
||||||
|
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
|
$this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
|
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1'));
|
||||||
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
||||||
|
|
||||||
|
$packageA->setRequires(array(new Link('A', 'B', null, 'requires')));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('A');
|
||||||
|
$this->request->updateAll();
|
||||||
|
|
||||||
|
$this->checkSolverResult(array(
|
||||||
|
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
|
||||||
|
array('job' => 'update', 'from' => $packageA, 'to' => $newPackageA),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
public function testSolverUpdateCurrent()
|
public function testSolverUpdateCurrent()
|
||||||
{
|
{
|
||||||
$this->repoInstalled->addPackage($this->getPackage('A', '1.0'));
|
$this->repoInstalled->addPackage($this->getPackage('A', '1.0'));
|
||||||
|
@ -181,7 +217,7 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
$this->request->install('A', new VersionConstraint('<', '2.0.0.0'));
|
$this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0'));
|
||||||
$this->request->update('A');
|
$this->request->update('A');
|
||||||
|
|
||||||
$this->checkSolverResult(array(array(
|
$this->checkSolverResult(array(array(
|
||||||
|
@ -198,8 +234,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
$this->request->install('A', new VersionConstraint('<', '2.0.0.0'));
|
$this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0'));
|
||||||
$this->request->update('A', new VersionConstraint('=', '1.0.0.0'));
|
$this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
|
||||||
|
|
||||||
$this->checkSolverResult(array(array(
|
$this->checkSolverResult(array(array(
|
||||||
'job' => 'update',
|
'job' => 'update',
|
||||||
|
@ -216,8 +252,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
$this->repo->addPackage($this->getPackage('A', '2.0'));
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
$this->request->install('A', new VersionConstraint('<', '2.0.0.0'));
|
$this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0'));
|
||||||
$this->request->update('A', new VersionConstraint('=', '1.0.0.0'));
|
$this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
|
||||||
|
|
||||||
$this->checkSolverResult(array(array(
|
$this->checkSolverResult(array(array(
|
||||||
'job' => 'update',
|
'job' => 'update',
|
||||||
|
@ -236,7 +272,7 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
||||||
$this->repo->addPackage($packageC = $this->getPackage('C', '1.1'));
|
$this->repo->addPackage($packageC = $this->getPackage('C', '1.1'));
|
||||||
$this->repo->addPackage($this->getPackage('D', '1.0'));
|
$this->repo->addPackage($this->getPackage('D', '1.0'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('<', '1.1'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -258,8 +294,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($middlePackageB = $this->getPackage('B', '1.0'));
|
$this->repo->addPackage($middlePackageB = $this->getPackage('B', '1.0'));
|
||||||
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
|
||||||
$this->repo->addPackage($oldPackageB = $this->getPackage('B', '0.9'));
|
$this->repo->addPackage($oldPackageB = $this->getPackage('B', '0.9'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('<', '1.1'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires')));
|
||||||
$packageA->setConflicts(array(new Link('A', 'B', new VersionConstraint('<', '1.0'), 'conflicts')));
|
$packageA->setConflicts(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.0'), 'conflicts')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -305,8 +341,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
||||||
$this->repo->addPackage($packageB = $this->getPackage('B', '0.8'));
|
$this->repo->addPackage($packageB = $this->getPackage('B', '0.8'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageQ->setProvides(array(new Link('Q', 'B', new VersionConstraint('=', '1.0'), 'provides')));
|
$packageQ->setProvides(array(new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -323,8 +359,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
||||||
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces')));
|
$packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -340,8 +376,8 @@ class SolverTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces')));
|
$packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -358,8 +394,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
|
||||||
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces')));
|
$packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -376,24 +412,24 @@ class SolverTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->repo->addPackage($packageX = $this->getPackage('X', '1.0'));
|
$this->repo->addPackage($packageX = $this->getPackage('X', '1.0'));
|
||||||
$packageX->setRequires(array(
|
$packageX->setRequires(array(
|
||||||
new Link('X', 'A', new VersionConstraint('>=', '2.0.0.0'), 'requires'),
|
new Link('X', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'),
|
||||||
new Link('X', 'B', new VersionConstraint('>=', '2.0.0.0'), 'requires')));
|
new Link('X', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires')));
|
||||||
|
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '2.0.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '2.0.0'));
|
||||||
$this->repo->addPackage($newPackageA = $this->getPackage('A', '2.1.0'));
|
$this->repo->addPackage($newPackageA = $this->getPackage('A', '2.1.0'));
|
||||||
$this->repo->addPackage($newPackageB = $this->getPackage('B', '2.1.0'));
|
$this->repo->addPackage($newPackageB = $this->getPackage('B', '2.1.0'));
|
||||||
|
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '2.0.0.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires')));
|
||||||
|
|
||||||
// new package A depends on version of package B that does not exist
|
// new package A depends on version of package B that does not exist
|
||||||
// => new package A is not installable
|
// => new package A is not installable
|
||||||
$newPackageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '2.2.0.0'), 'requires')));
|
$newPackageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '2.2.0.0'), 'requires')));
|
||||||
|
|
||||||
// add a package S replacing both A and B, so that S and B or S and A cannot be simultaneously installed
|
// add a package S replacing both A and B, so that S and B or S and A cannot be simultaneously installed
|
||||||
// but an alternative option for A and B both exists
|
// but an alternative option for A and B both exists
|
||||||
// this creates a more difficult so solve conflict
|
// this creates a more difficult so solve conflict
|
||||||
$this->repo->addPackage($packageS = $this->getPackage('S', '2.0.0'));
|
$this->repo->addPackage($packageS = $this->getPackage('S', '2.0.0'));
|
||||||
$packageS->setReplaces(array(new Link('S', 'A', new VersionConstraint('>=', '2.0.0.0'), 'replaces'), new Link('S', 'B', new VersionConstraint('>=', '2.0.0.0'), 'replaces')));
|
$packageS->setReplaces(array(new Link('S', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces'), new Link('S', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -411,8 +447,8 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageB1 = $this->getPackage('B', '0.9'));
|
$this->repo->addPackage($packageB1 = $this->getPackage('B', '0.9'));
|
||||||
$this->repo->addPackage($packageB2 = $this->getPackage('B', '1.1'));
|
$this->repo->addPackage($packageB2 = $this->getPackage('B', '1.1'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageB2->setRequires(array(new Link('B', 'A', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageB2->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -426,16 +462,17 @@ class SolverTest extends TestCase
|
||||||
|
|
||||||
public function testInstallAlternativeWithCircularRequire()
|
public function testInstallAlternativeWithCircularRequire()
|
||||||
{
|
{
|
||||||
$this->markTestIncomplete();
|
|
||||||
|
|
||||||
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
$this->repo->addPackage($packageC = $this->getPackage('C', '1.0'));
|
$this->repo->addPackage($packageC = $this->getPackage('C', '1.0'));
|
||||||
$this->repo->addPackage($packageD = $this->getPackage('D', '1.0'));
|
$this->repo->addPackage($packageD = $this->getPackage('D', '1.0'));
|
||||||
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageB->setRequires(array(new Link('B', 'Virtual', new VersionConstraint('>=', '1.0'), 'requires')));
|
$packageB->setRequires(array(new Link('B', 'Virtual', $this->getVersionConstraint('>=', '1.0'), 'requires')));
|
||||||
$packageC->setRequires(array(new Link('C', 'Virtual', new VersionConstraint('==', '1.0'), 'provides')));
|
$packageC->setProvides(array(new Link('C', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides')));
|
||||||
$packageD->setRequires(array(new Link('D', 'Virtual', new VersionConstraint('==', '1.0'), 'provides')));
|
$packageD->setProvides(array(new Link('D', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides')));
|
||||||
|
|
||||||
|
$packageC->setRequires(array(new Link('C', 'A', $this->getVersionConstraint('==', '1.0'), 'requires')));
|
||||||
|
$packageD->setRequires(array(new Link('D', 'A', $this->getVersionConstraint('==', '1.0'), 'requires')));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
|
||||||
|
@ -460,18 +497,18 @@ class SolverTest extends TestCase
|
||||||
$this->repo->addPackage($packageD2 = $this->getPackage('D', '1.1'));
|
$this->repo->addPackage($packageD2 = $this->getPackage('D', '1.1'));
|
||||||
|
|
||||||
$packageA->setRequires(array(
|
$packageA->setRequires(array(
|
||||||
new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'),
|
new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'),
|
||||||
new Link('A', 'C', new VersionConstraint('>=', '1.0'), 'requires'),
|
new Link('A', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$packageD->setReplaces(array(
|
$packageD->setReplaces(array(
|
||||||
new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'),
|
new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'),
|
||||||
new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'),
|
new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$packageD2->setReplaces(array(
|
$packageD2->setReplaces(array(
|
||||||
new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'),
|
new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'),
|
||||||
new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'),
|
new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->reposComplete();
|
$this->reposComplete();
|
||||||
|
@ -484,6 +521,83 @@ class SolverTest extends TestCase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIssue265()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($packageA1 = $this->getPackage('A', '2.0.999999-dev'));
|
||||||
|
$this->repo->addPackage($packageA2 = $this->getPackage('A', '2.1-dev'));
|
||||||
|
$this->repo->addPackage($packageA3 = $this->getPackage('A', '2.2-dev'));
|
||||||
|
$this->repo->addPackage($packageB1 = $this->getPackage('B', '2.0.10'));
|
||||||
|
$this->repo->addPackage($packageB2 = $this->getPackage('B', '2.0.9'));
|
||||||
|
$this->repo->addPackage($packageC = $this->getPackage('C', '2.0-dev'));
|
||||||
|
$this->repo->addPackage($packageD = $this->getPackage('D', '2.0.9'));
|
||||||
|
|
||||||
|
$packageC->setRequires(array(
|
||||||
|
new Link('C', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'),
|
||||||
|
new Link('C', 'D', $this->getVersionConstraint('>=', '2.0'), 'requires'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$packageD->setRequires(array(
|
||||||
|
new Link('D', 'A', $this->getVersionConstraint('>=', '2.1'), 'requires'),
|
||||||
|
new Link('D', 'B', $this->getVersionConstraint('>=', '2.0-dev'), 'requires'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$packageB1->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires')));
|
||||||
|
$packageB2->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires')));
|
||||||
|
|
||||||
|
$packageB2->setReplaces(array(new Link('B', 'D', $this->getVersionConstraint('==', '2.0.9.0'), 'replaces')));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('C', $this->getVersionConstraint('==', '2.0.0.0-dev'));
|
||||||
|
|
||||||
|
$this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
|
||||||
|
|
||||||
|
$this->solver->solve($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConflictResultEmpty()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));;
|
||||||
|
|
||||||
|
$packageA->setConflicts(array(
|
||||||
|
new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'conflicts'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('A');
|
||||||
|
$this->request->install('B');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$transaction = $this->solver->solve($this->request);
|
||||||
|
$this->fail('Unsolvable conflict did not resolve in exception.');
|
||||||
|
} catch (SolverProblemsException $e) {
|
||||||
|
// TODO assert problem properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsatisfiableRequires()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
|
|
||||||
|
$packageA->setRequires(array(
|
||||||
|
new Link('A', 'B', $this->getVersionConstraint('>=', '2.0'), 'requires'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('A');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$transaction = $this->solver->solve($this->request);
|
||||||
|
$this->fail('Unsolvable conflict did not resolve in exception.');
|
||||||
|
} catch (SolverProblemsException $e) {
|
||||||
|
// TODO assert problem properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function reposComplete()
|
protected function reposComplete()
|
||||||
{
|
{
|
||||||
$this->pool->addRepository($this->repoInstalled);
|
$this->pool->addRepository($this->repoInstalled);
|
||||||
|
@ -513,5 +627,4 @@ class SolverTest extends TestCase
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,9 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('getPackages')
|
->method('getPackages')
|
||||||
->will($this->returnValue(array($this->packages[0])));
|
->will($this->returnValue(array($this->packages[0])));
|
||||||
$this->repository
|
$this->repository
|
||||||
->expects($this->once())
|
->expects($this->exactly(2))
|
||||||
->method('hasPackage')
|
->method('hasPackage')
|
||||||
->will($this->returnValue(true));
|
->will($this->onConsecutiveCalls(true, false));
|
||||||
$installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im);
|
$installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im);
|
||||||
|
|
||||||
$test = $this;
|
$test = $this;
|
||||||
|
@ -90,9 +90,9 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('getPackages')
|
->method('getPackages')
|
||||||
->will($this->returnValue(array($this->packages[1])));
|
->will($this->returnValue(array($this->packages[1])));
|
||||||
$this->repository
|
$this->repository
|
||||||
->expects($this->once())
|
->expects($this->exactly(2))
|
||||||
->method('hasPackage')
|
->method('hasPackage')
|
||||||
->will($this->returnValue(true));
|
->will($this->onConsecutiveCalls(true, false));
|
||||||
$installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im);
|
$installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im);
|
||||||
|
|
||||||
$test = $this;
|
$test = $this;
|
||||||
|
|
|
@ -128,10 +128,9 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
|
||||||
->will($this->returnValue('package1'));
|
->will($this->returnValue('package1'));
|
||||||
|
|
||||||
$this->repository
|
$this->repository
|
||||||
->expects($this->exactly(2))
|
->expects($this->exactly(3))
|
||||||
->method('hasPackage')
|
->method('hasPackage')
|
||||||
->with($initial)
|
->will($this->onConsecutiveCalls(true, false, false));
|
||||||
->will($this->onConsecutiveCalls(true, false));
|
|
||||||
|
|
||||||
$this->dm
|
$this->dm
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
|
|
|
@ -49,9 +49,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
|
||||||
'parses datetime' => array('20100102-203040', '20100102-203040'),
|
'parses datetime' => array('20100102-203040', '20100102-203040'),
|
||||||
'parses dt+number' => array('20100102203040-10', '20100102203040-10'),
|
'parses dt+number' => array('20100102203040-10', '20100102203040-10'),
|
||||||
'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'),
|
'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'),
|
||||||
'parses master' => array('master', '9999999-dev'),
|
'parses master' => array('dev-master', '9999999-dev'),
|
||||||
'parses trunk' => array('trunk', '9999999-dev'),
|
'parses trunk' => array('dev-trunk', '9999999-dev'),
|
||||||
'parses trunk/2' => array('trunk-dev', '9999999-dev'),
|
'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'),
|
||||||
|
'parses arbitrary2' => array('DEV-FOOBAR', 'dev-foobar'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
|
||||||
'invalid chars' => array('a'),
|
'invalid chars' => array('a'),
|
||||||
'invalid type' => array('1.0.0-meh'),
|
'invalid type' => array('1.0.0-meh'),
|
||||||
'too many bits' => array('1.0.0.0.0'),
|
'too many bits' => array('1.0.0.0.0'),
|
||||||
|
'non-dev arbitrary' => array('feature-foo'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +99,8 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
|
||||||
'parses long digits/2' => array('2.4.4', '2.4.4.9999999-dev'),
|
'parses long digits/2' => array('2.4.4', '2.4.4.9999999-dev'),
|
||||||
'parses master' => array('master', '9999999-dev'),
|
'parses master' => array('master', '9999999-dev'),
|
||||||
'parses trunk' => array('trunk', '9999999-dev'),
|
'parses trunk' => array('trunk', '9999999-dev'),
|
||||||
|
'parses arbitrary' => array('feature-a', 'dev-feature-a'),
|
||||||
|
'parses arbitrary/2' => array('foobar', 'dev-foobar'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +125,9 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
|
||||||
'no op means eq' => array('1.2.3', new VersionConstraint('=', '1.2.3.0')),
|
'no op means eq' => array('1.2.3', new VersionConstraint('=', '1.2.3.0')),
|
||||||
'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')),
|
'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')),
|
||||||
'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')),
|
'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')),
|
||||||
'accepts master' => array('>=master-dev', new VersionConstraint('>=', '9999999-dev')),
|
'accepts master' => array('>=dev-master', new VersionConstraint('>=', '9999999-dev')),
|
||||||
'accepts master/2' => array('master-dev', new VersionConstraint('=', '9999999-dev')),
|
'accepts master/2' => array('dev-master', new VersionConstraint('=', '9999999-dev')),
|
||||||
|
'accepts arbitrary' => array('dev-feature-a', new VersionConstraint('=', 'dev-feature-a')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
<?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\Test\Json;
|
||||||
|
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
|
use Composer\Package\Dumper\ArrayDumper;
|
||||||
|
use Composer\Repository\VcsRepository;
|
||||||
|
use Composer\Repository\Vcs\GitDriver;
|
||||||
|
use Composer\Util\Filesystem;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Composer\IO\NullIO;
|
||||||
|
|
||||||
|
class VcsRepositoryTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private static $gitRepo;
|
||||||
|
private static $skipped;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
$oldCwd = getcwd();
|
||||||
|
self::$gitRepo = sys_get_temp_dir() . '/composer-git-'.rand().'/';
|
||||||
|
|
||||||
|
$locator = new ExecutableFinder();
|
||||||
|
if (!$locator->find('git')) {
|
||||||
|
self::$skipped = 'This test needs a git binary in the PATH to be able to run';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mkdir(self::$gitRepo) || !chdir(self::$gitRepo)) {
|
||||||
|
self::$skipped = 'Could not create and move into the temp git repo '.self::$gitRepo;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
$process = new ProcessExecutor;
|
||||||
|
$process->execute('git init', $null);
|
||||||
|
touch('foo');
|
||||||
|
$process->execute('git add foo', $null);
|
||||||
|
$process->execute('git commit -m init', $null);
|
||||||
|
|
||||||
|
// non-composed tag & branch
|
||||||
|
$process->execute('git tag 0.5.0', $null);
|
||||||
|
$process->execute('git branch oldbranch', $null);
|
||||||
|
|
||||||
|
// add composed tag & master branch
|
||||||
|
$composer = array('name' => 'a/b');
|
||||||
|
file_put_contents('composer.json', json_encode($composer));
|
||||||
|
$process->execute('git add composer.json', $null);
|
||||||
|
$process->execute('git commit -m addcomposer', $null);
|
||||||
|
$process->execute('git tag 0.6.0', $null);
|
||||||
|
|
||||||
|
// add feature-a branch
|
||||||
|
$process->execute('git checkout -b feature-a', $null);
|
||||||
|
file_put_contents('foo', 'bar feature');
|
||||||
|
$process->execute('git add foo', $null);
|
||||||
|
$process->execute('git commit -m change-a', $null);
|
||||||
|
|
||||||
|
// add version to composer.json
|
||||||
|
$process->execute('git checkout master', $null);
|
||||||
|
$composer['version'] = '1.0.0';
|
||||||
|
file_put_contents('composer.json', json_encode($composer));
|
||||||
|
$process->execute('git add composer.json', $null);
|
||||||
|
$process->execute('git commit -m addversion', $null);
|
||||||
|
|
||||||
|
// create tag with wrong version in it
|
||||||
|
$process->execute('git tag 0.9.0', $null);
|
||||||
|
// create tag with correct version in it
|
||||||
|
$process->execute('git tag 1.0.0', $null);
|
||||||
|
|
||||||
|
// add feature-b branch
|
||||||
|
$process->execute('git checkout -b feature-b', $null);
|
||||||
|
file_put_contents('foo', 'baz feature');
|
||||||
|
$process->execute('git add foo', $null);
|
||||||
|
$process->execute('git commit -m change-b', $null);
|
||||||
|
|
||||||
|
// add 1.0 branch
|
||||||
|
$process->execute('git checkout master', $null);
|
||||||
|
$process->execute('git branch 1.0', $null);
|
||||||
|
|
||||||
|
// add 1.0.x branch
|
||||||
|
$process->execute('git branch 1.0.x', $null);
|
||||||
|
|
||||||
|
// update master to 2.0
|
||||||
|
$composer['version'] = '2.0.0';
|
||||||
|
file_put_contents('composer.json', json_encode($composer));
|
||||||
|
$process->execute('git add composer.json', $null);
|
||||||
|
$process->execute('git commit -m bump-version', $null);
|
||||||
|
|
||||||
|
chdir($oldCwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
if (self::$skipped) {
|
||||||
|
$this->markTestSkipped(self::$skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass()
|
||||||
|
{
|
||||||
|
$fs = new Filesystem;
|
||||||
|
$fs->removeDirectory(self::$gitRepo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoadVersions()
|
||||||
|
{
|
||||||
|
$expected = array(
|
||||||
|
'0.6.0' => true,
|
||||||
|
'1.0.0' => true,
|
||||||
|
'1.0-dev' => true,
|
||||||
|
'1.0.x-dev' => true,
|
||||||
|
'dev-feature-b' => true,
|
||||||
|
'dev-feature-a' => true,
|
||||||
|
'dev-master' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
$repo = new VcsRepository(array('url' => self::$gitRepo), new NullIO);
|
||||||
|
$packages = $repo->getPackages();
|
||||||
|
$dumper = new ArrayDumper();
|
||||||
|
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
if (isset($expected[$package->getPrettyVersion()])) {
|
||||||
|
unset($expected[$package->getPrettyVersion()]);
|
||||||
|
} else {
|
||||||
|
$this->fail('Unexpected version '.$package->getPrettyVersion().' in '.json_encode($dumper->dump($package)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEmpty($expected, 'Missing versions: '.implode(', ', array_keys($expected)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,43 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of Composer.
|
* This file is part of Composer.
|
||||||
*
|
*
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
*
|
*
|
||||||
* For the full copyright and license information, please view the LICENSE
|
* For the full copyright and license information, please view the LICENSE
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Composer\Test;
|
namespace Composer\Test;
|
||||||
|
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Package\MemoryPackage;
|
use Composer\Package\MemoryPackage;
|
||||||
|
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||||
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||||
private static $versionParser;
|
{
|
||||||
|
private static $versionParser;
|
||||||
public static function setUpBeforeClass()
|
|
||||||
{
|
public static function setUpBeforeClass()
|
||||||
if (!self::$versionParser) {
|
{
|
||||||
self::$versionParser = new VersionParser();
|
if (!self::$versionParser) {
|
||||||
}
|
self::$versionParser = new VersionParser();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected function getPackage($name, $version)
|
|
||||||
{
|
protected function getVersionConstraint($operator, $version)
|
||||||
$normVersion = self::$versionParser->normalize($version);
|
{
|
||||||
return new MemoryPackage($name, $normVersion, $version);
|
return new VersionConstraint(
|
||||||
}
|
$operator,
|
||||||
}
|
self::$versionParser->normalize($version)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPackage($name, $version)
|
||||||
|
{
|
||||||
|
$normVersion = self::$versionParser->normalize($version);
|
||||||
|
return new MemoryPackage($name, $normVersion, $version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue