From 61d24acaa28a3c4875373497ccff9315edb3d16f Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 13:34:07 +0100 Subject: [PATCH 01/77] [docs] introduction chapter --- {doc => Resources}/composer-schema.json | 0 doc/00-intro.md | 65 +++++++++++++++++++++++++ doc/{ => dev}/DefaultPolicy.md | 0 3 files changed, 65 insertions(+) rename {doc => Resources}/composer-schema.json (100%) create mode 100644 doc/00-intro.md rename doc/{ => dev}/DefaultPolicy.md (100%) diff --git a/doc/composer-schema.json b/Resources/composer-schema.json similarity index 100% rename from doc/composer-schema.json rename to Resources/composer-schema.json diff --git a/doc/00-intro.md b/doc/00-intro.md new file mode 100644 index 000000000..8daaac2a3 --- /dev/null +++ b/doc/00-intro.md @@ -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 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.2" + } +} +``` + +We are simply stating that our project requires the `monolog/monolog` package, +version `1.0.2`. + +## 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. diff --git a/doc/DefaultPolicy.md b/doc/dev/DefaultPolicy.md similarity index 100% rename from doc/DefaultPolicy.md rename to doc/dev/DefaultPolicy.md From cbc021c05dc5182f0af578608f2750c892acf681 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sat, 18 Feb 2012 18:04:45 +0100 Subject: [PATCH 02/77] featureRule no longer exists, only update rule --- src/Composer/DependencyResolver/Solver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 8f12e6e0a..680b87591 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -988,7 +988,7 @@ class Solver if ($this->policy->allowUninstall()) { $rule->setWeak(true); - $this->addRule(RuleSet::TYPE_FEATURE, $featureRule); + $this->addRule(RuleSet::TYPE_FEATURE, $rule); $this->packageToFeatureRule[$package->getId()] = $rule; } else { $this->addRule(RuleSet::TYPE_UPDATE, $rule); From e825ceaea0f13b23dbf0d996354e8fa8df344cb8 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 18:11:00 +0100 Subject: [PATCH 03/77] [docs] basic usage chapter --- doc/00-intro.md | 4 +- doc/01-basic-usage.md | 164 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 doc/01-basic-usage.md diff --git a/doc/00-intro.md b/doc/00-intro.md index 8daaac2a3..ec4ccc6b4 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -29,13 +29,13 @@ which describes the project's dependencies. ```json { "require": { - "monolog/monolog": "1.0.2" + "monolog/monolog": "1.0.*" } } ``` We are simply stating that our project requires the `monolog/monolog` package, -version `1.0.2`. +any version beginning with `1.0`. ## Installation diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md new file mode 100644 index 000000000..169f92d10 --- /dev/null +++ b/doc/01-basic-usage.md @@ -0,0 +1,164 @@ +# 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. + +If you don't know what to use as a vendor name, your GitHub username is usually +a good bet. + +## 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`. Youcan define multiple of these, separated by comma: + `>=1.0,<2.0`. + +* **Wildcard:** You can specify a pattern with a `*` wildcard. The previous + `>=1.0,<2.0` example could also be written as `1.0.*`. + +## 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 + +## 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. From b56ae1a48426f5ec91861eca505c2b5f7826224a Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 18:15:57 +0100 Subject: [PATCH 04/77] [docs] add section about packagist --- doc/01-basic-usage.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 169f92d10..2da41c801 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -122,6 +122,14 @@ 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 From ec61297122258f21a030d54844f3c9827bef1e3d Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Sat, 18 Feb 2012 18:31:19 +0100 Subject: [PATCH 05/77] Remove the allowUninstall policy option and clean up the only usage --- src/Composer/DependencyResolver/DefaultPolicy.php | 5 ----- src/Composer/DependencyResolver/PolicyInterface.php | 1 - src/Composer/DependencyResolver/Solver.php | 11 +++-------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index f06b4104b..63699d883 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -21,11 +21,6 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class DefaultPolicy implements PolicyInterface { - public function allowUninstall() - { - return true; - } - public function versionCompare(PackageInterface $a, PackageInterface $b, $operator) { $constraint = new VersionConstraint($operator, $b->getVersion()); diff --git a/src/Composer/DependencyResolver/PolicyInterface.php b/src/Composer/DependencyResolver/PolicyInterface.php index 45309a081..7c26ab63f 100644 --- a/src/Composer/DependencyResolver/PolicyInterface.php +++ b/src/Composer/DependencyResolver/PolicyInterface.php @@ -20,7 +20,6 @@ use Composer\Package\PackageInterface; */ interface PolicyInterface { - function allowUninstall(); function versionCompare(PackageInterface $a, PackageInterface $b, $operator); function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package); function installable(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 680b87591..25d186073 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -986,14 +986,9 @@ class Solver $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package); $rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package); - if ($this->policy->allowUninstall()) { - $rule->setWeak(true); - $this->addRule(RuleSet::TYPE_FEATURE, $rule); - $this->packageToFeatureRule[$package->getId()] = $rule; - } else { - $this->addRule(RuleSet::TYPE_UPDATE, $rule); - $this->packageToUpdateRule[$package->getId()] = $rule; - } + $rule->setWeak(true); + $this->addRule(RuleSet::TYPE_FEATURE, $rule); + $this->packageToFeatureRule[$package->getId()] = $rule; } foreach ($this->jobs as $job) { From 33f49462bd4944cf1db4f1927642c37e0b0382f8 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 18:42:06 +0100 Subject: [PATCH 06/77] [docs] adjust wildcard version constraint explanation --- doc/01-basic-usage.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 2da41c801..10e81cbfd 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -78,11 +78,10 @@ Version constraints can be specified in a few different ways. 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`. Youcan define multiple of these, separated by comma: + 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. The previous - `>=1.0,<2.0` example could also be written as `1.0.*`. +* **Wildcard:** You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0,<1.1-dev`. ## Installing dependencies From 3fb75faa7589e8967a63ca420319b19225e99aff Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Sat, 18 Feb 2012 19:19:49 +0100 Subject: [PATCH 07/77] Now that no more update rules are created the code that handles them can be removed too. Also adapted the tests that used TYPE_UPDATE exemplarily to use TYPE_FEATURE. --- src/Composer/DependencyResolver/RuleSet.php | 2 -- src/Composer/DependencyResolver/Solver.php | 17 ++--------------- .../RuleSetIteratorTest.php | 6 +++--- .../Test/DependencyResolver/RuleSetTest.php | 19 +++++++++---------- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 6d79a9678..1f444c788 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -20,7 +20,6 @@ class RuleSet implements \IteratorAggregate, \Countable // highest priority => lowest number const TYPE_PACKAGE = 0; const TYPE_JOB = 1; - const TYPE_UPDATE = 2; const TYPE_FEATURE = 3; const TYPE_CHOICE = 4; const TYPE_LEARNED = 5; @@ -29,7 +28,6 @@ class RuleSet implements \IteratorAggregate, \Countable -1 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', self::TYPE_FEATURE => 'FEATURE', - self::TYPE_UPDATE => 'UPDATE', self::TYPE_JOB => 'JOB', self::TYPE_CHOICE => 'CHOICE', self::TYPE_LEARNED => 'LEARNED', diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 25d186073..9ff6b0764 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -52,7 +52,6 @@ class Solver protected $decisionMap; protected $installedMap; - protected $packageToUpdateRule = array(); protected $packageToFeatureRule = array(); public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) @@ -508,7 +507,7 @@ class Solver // push all of our rules (can only be feature or job rules) // 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()) { continue; } @@ -882,11 +881,6 @@ class Solver 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()])) { $this->packageToFeatureRule[$package->getId()]->disable(); } @@ -1056,9 +1050,6 @@ class Solver if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) { $literals = array(); - if (isset($this->packageToUpdateRule[$package->getId()])) { - $literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals()); - } if (isset($this->packageToFeatureRule[$package->getId()])) { $literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals()); } @@ -1808,11 +1799,7 @@ class Solver $rule = null; - if (isset($this->packageToUpdateRule[$literal->getPackageId()])) { - $rule = $this->packageToUpdateRule[$literal->getPackageId()]; - } - - if ((!$rule || $rule->isDisabled()) && isset($this->packageToFeatureRule[$literal->getPackageId()])) { + if (isset($this->packageToFeatureRule[$literal->getPackageId()])) { $rule = $this->packageToFeatureRule[$literal->getPackageId()]; } diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 6340a7f68..28db18131 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -27,7 +27,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_UPDATE => array( + RuleSet::TYPE_FEATURE => array( new Rule(array(), 'update1', null), ), RuleSet::TYPE_PACKAGE => array(), @@ -46,7 +46,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( $this->rules[RuleSet::TYPE_JOB][0], $this->rules[RuleSet::TYPE_JOB][1], - $this->rules[RuleSet::TYPE_UPDATE][0], + $this->rules[RuleSet::TYPE_FEATURE][0], ); $this->assertEquals($expected, $result); @@ -64,7 +64,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase $expected = array( RuleSet::TYPE_JOB, RuleSet::TYPE_JOB, - RuleSet::TYPE_UPDATE, + RuleSet::TYPE_FEATURE, ); $this->assertEquals($expected, $result); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 7a5b018b4..54ad88a58 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -27,10 +27,9 @@ class RuleSetTest extends TestCase new Rule(array(), 'job1', null), new Rule(array(), 'job2', null), ), - RuleSet::TYPE_UPDATE => array( + RuleSet::TYPE_FEATURE => array( new Rule(array(), 'update1', null), ), - RuleSet::TYPE_FEATURE => array(), RuleSet::TYPE_LEARNED => array(), RuleSet::TYPE_CHOICE => array(), ); @@ -38,7 +37,7 @@ class RuleSetTest extends TestCase $ruleSet = new RuleSet; $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); $this->assertEquals($rules, $ruleSet->getRules()); @@ -81,7 +80,7 @@ class RuleSetTest extends TestCase $rule1 = new Rule(array(), 'job1', null); $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_UPDATE); + $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); $iterator = $ruleSet->getIterator(); @@ -97,9 +96,9 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $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()); } @@ -111,7 +110,7 @@ class RuleSetTest extends TestCase $rule2 = new Rule(array(), 'job1', null); $ruleSet->add($rule1, RuleSet::TYPE_JOB); - $ruleSet->add($rule2, RuleSet::TYPE_UPDATE); + $ruleSet->add($rule2, RuleSet::TYPE_FEATURE); $iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB); @@ -143,7 +142,7 @@ class RuleSetTest extends TestCase ->method('equal') ->will($this->returnValue(false)); - $ruleSet->add($rule, RuleSet::TYPE_UPDATE); + $ruleSet->add($rule, RuleSet::TYPE_FEATURE); $this->assertTrue($ruleSet->containsEqual($rule)); $this->assertFalse($ruleSet->containsEqual($rule2)); @@ -156,9 +155,9 @@ class RuleSetTest extends TestCase $literal = new Literal($this->getPackage('foo', '2.1'), true); $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() From f4984e9a1e208c75bd1d7955b0fc29dce616acac Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 20:03:24 +0100 Subject: [PATCH 08/77] [docs] first parts of the libraries chapter --- doc/01-basic-usage.md | 3 --- doc/02-libraries.md | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 doc/02-libraries.md diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 10e81cbfd..13180bca8 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -64,9 +64,6 @@ 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. -If you don't know what to use as a vendor name, your GitHub username is usually -a good bet. - ## Package versions We are also requiring the version `1.0.*` of monolog. This means any version diff --git a/doc/02-libraries.md b/doc/02-libraries.md new file mode 100644 index 000000000..48632489e --- /dev/null +++ b/doc/02-libraries.md @@ -0,0 +1,62 @@ +# 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. The convention for word separation is to use dashes. + +## 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.* From f54b04aac84a91d168322247adaea1b1b3d06aca Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 23:28:47 +0100 Subject: [PATCH 09/77] Restore realpath behavior --- src/Composer/Installer/LibraryInstaller.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 1f94f4071..a3f0b2b74 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -78,11 +78,9 @@ class LibraryInstaller implements InstallerInterface */ public function install(PackageInterface $package) { + $this->initializeDirs(); $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 if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) { $this->removeBinaries($package); @@ -104,11 +102,9 @@ class LibraryInstaller implements InstallerInterface throw new \InvalidArgumentException('Package is not installed: '.$initial); } + $this->initializeDirs(); $downloadPath = $this->getInstallPath($initial); - $this->filesystem->ensureDirectoryExists($this->vendorDir); - $this->filesystem->ensureDirectoryExists($this->binDir); - $this->removeBinaries($initial); $this->downloadManager->update($initial, $target, $downloadPath); $this->installBinaries($target); @@ -191,6 +187,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) { $binPath = $this->filesystem->findShortestPath($link, $bin); From 868fc255b140d9784765a7ee38dfdcaab905513d Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 18 Feb 2012 23:43:23 +0100 Subject: [PATCH 10/77] [docs] add section about publishing to vcs and on packagist --- doc/02-libraries.md | 55 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 48632489e..50c74b681 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -19,7 +19,7 @@ In order to make that package installable you need to give it a name. You do thi 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. The convention for word separation is to use dashes. +**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 @@ -60,3 +60,56 @@ Here are some examples of version branch names: 1.* 1.1.x 1.1.* + +**Note:** When you install a dev version, it will install it from source. See [Repositories] for more information. + +## 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 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, at which point packagist will start crawling it. Once it is done, your package will be available to anyone. From 8fd4e560292c68db8322b2a5fe3d14c083f86551 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:13:21 +0100 Subject: [PATCH 11/77] Deciding to install a package and wanting to install it, is not a conflict --- src/Composer/DependencyResolver/Solver.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 9ff6b0764..281685a34 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1113,6 +1113,8 @@ class Solver protected function addDecision(Literal $l, $level) { + assert($this->decisionMap[$l->getPackageId()] == 0); + if ($l->isWanted()) { $this->decisionMap[$l->getPackageId()] = $level; } else { @@ -1123,6 +1125,9 @@ class Solver protected function addDecisionId($literalId, $level) { $packageId = abs($literalId); + + assert($this->decisionMap[$packageId] == 0); + if ($literalId > 0) { $this->decisionMap[$packageId] = $level; } else { @@ -1165,8 +1170,8 @@ class Solver { $packageId = abs($literalId); 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) ); } From 52d876e11e65a6b4b3780ac5c6ff1083b916d6aa Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:15:23 +0100 Subject: [PATCH 12/77] Add SolverProblemsException and test basic solver failures --- src/Composer/DependencyResolver/Solver.php | 4 ++ .../SolverProblemsException.php | 65 +++++++++++++++++++ .../Test/DependencyResolver/SolverTest.php | 43 +++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/Composer/DependencyResolver/SolverProblemsException.php diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 281685a34..4359f3102 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1035,6 +1035,10 @@ class Solver //findrecommendedsuggested(solv); //solver_prepare_solutions(solv); + if ($this->problems) { + throw new SolverProblemsException($this->problems, $this->learnedPool); + } + return $this->createTransaction(); } diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php new file mode 100644 index 000000000..cbc4fd571 --- /dev/null +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -0,0 +1,65 @@ + + * Jordi Boggiano + * + * 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 + */ +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; + } +} diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index a71feafce..bea9d989b 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -19,6 +19,7 @@ use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; +use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Test\TestCase; @@ -484,6 +485,47 @@ class SolverTest extends TestCase )); } + 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', new VersionConstraint('>=', '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) { + } + } + + 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', new VersionConstraint('>=', '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) { + } + } + protected function reposComplete() { $this->pool->addRepository($this->repoInstalled); @@ -513,5 +555,4 @@ class SolverTest extends TestCase $this->assertEquals($expected, $result); } - } From e6143d1584a1bd9d344d966b2bd5ebb6b8b175bb Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:21:39 +0100 Subject: [PATCH 13/77] Add todos to explain why try/catch is inside the test --- tests/Composer/Test/DependencyResolver/SolverTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index bea9d989b..18cb9f4a1 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -503,6 +503,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { + // @todo: assert problem properties } } @@ -523,6 +524,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { + // @todo: assert problem properties } } From 7a66eb3367b27d90d12dc80fe8494c0cefb5e05c Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 13:12:34 +0100 Subject: [PATCH 14/77] [docs] add a note about the lock file to the libs chapter --- doc/02-libraries.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 50c74b681..53366c45c 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -63,6 +63,14 @@ Here are some examples of version branch names: **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`. From eafe13d5a5eae6b9c18b4f5a8a893b4964be73ea Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Sun, 19 Feb 2012 14:55:14 +0100 Subject: [PATCH 15/77] Create an impossible rule when trying to install something that doesn't exist. The rule isn't acted upon yet (hence the incomplete test) but it is there as a reminder that this case needs to be handled. --- src/Composer/DependencyResolver/Solver.php | 6 ++++++ .../Test/DependencyResolver/SolverTest.php | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 4359f3102..a395ad969 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -964,6 +964,12 @@ class Solver foreach ($this->jobs as $job) { + if (empty($job['packages']) && $job['cmd'] == 'install') { + $this->addRule( + RuleSet::TYPE_JOB, + $this->createImpossibleRule(static::RULE_JOB_INSTALL, $job) + ); + } foreach ($job['packages'] as $package) { switch ($job['cmd']) { case 'install': diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 18cb9f4a1..5ad7e90c3 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -55,6 +55,22 @@ 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->markTestIncomplete('Reporting this failure is not implemented/working yet'); + //$this->fail('Unsolvable conflict did not resolve in exception.'); + } catch (SolverProblemsException $e) { + // @todo: assert problem properties + } + } + public function testSolverInstallWithDeps() { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); From fd0bf14eb2ccbda2487a34eeda43fd2d4c1a970e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 15:04:40 +0100 Subject: [PATCH 16/77] Support both require and requires as depends link-type arg --- src/Composer/Command/DependsCommand.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index f695487ff..82ff5e02d 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -25,6 +25,8 @@ use Symfony\Component\Console\Output\OutputInterface; */ class DependsCommand extends Command { + protected $linkTypes = array('require', 'recommend', 'suggest'); + protected function configure() { $this @@ -32,7 +34,7 @@ class DependsCommand extends Command ->setDescription('Shows which packages depend on the given package') ->setDefinition(array( 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(<<getRepositoryManager()->getRepositories(); $types = $input->getOption('link-type'); + foreach ($repos as $repository) { foreach ($repository->getPackages() as $package) { 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 ($verbose) { $references[] = array($type, $package, $link); From 57c13df37eb9ff0be19304a7b87f7510cf53f070 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 15:16:25 +0100 Subject: [PATCH 17/77] [docs] chapter about the cli --- doc/03-cli.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 doc/03-cli.md diff --git a/doc/03-cli.md b/doc/03-cli.md new file mode 100644 index 000000000..56989c466 --- /dev/null +++ b/doc/03-cli.md @@ -0,0 +1,119 @@ +# 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 From 0b8fb68e9301a719b4ea664a14d1a6c7bba2fb5f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:19:34 +0100 Subject: [PATCH 18/77] Add a debug print method for the entire watch tree to the solver --- src/Composer/DependencyResolver/Solver.php | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 4359f3102..16295fd5d 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -2033,4 +2033,42 @@ class Solver } 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*"; + } + } + else if ($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"; + } + } } From 8484199677ecdc5aa54f073aa6938fa723e8d08c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:20:13 +0100 Subject: [PATCH 19/77] Display undecided literals as undecided with a ?, when printing the decision map --- src/Composer/DependencyResolver/Solver.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 16295fd5d..833548246 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -2018,8 +2018,10 @@ class Solver } if ($level > 0) { echo ' +' . $this->pool->packageById($packageId)."\n"; - } else { + } elseif ($level < 0) { echo ' -' . $this->pool->packageById($packageId)."\n"; + } else { + echo ' ?' . $this->pool->packageById($packageId)."\n"; } } echo "\n"; From e20a79ac61de55a8000a24b1926045494d499004 Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Sun, 19 Feb 2012 15:30:53 +0100 Subject: [PATCH 20/77] Fix TODO tags to confirm with the projects standard --- src/Composer/DependencyResolver/Solver.php | 2 +- tests/Composer/Test/DependencyResolver/SolverTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index a395ad969..6d85cb325 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -304,7 +304,7 @@ class Solver // if ignoreinstalledsobsoletes is not set, we're also checking // obsoletes of installed packages (like newer rpm versions) // - /** @TODO: if ($this->noInstalledObsoletes) */ + /** TODO if ($this->noInstalledObsoletes) */ if (true) { $noObsoletes = isset($this->noObsoletes[$package->getId()]); $isInstalled = (isset($this->installedMap[$package->getId()])); diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 5ad7e90c3..875dca41c 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -67,7 +67,7 @@ class SolverTest extends TestCase $this->markTestIncomplete('Reporting this failure is not implemented/working yet'); //$this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { - // @todo: assert problem properties + // TODO assert problem properties } } @@ -519,7 +519,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { - // @todo: assert problem properties + // TODO assert problem properties } } @@ -540,7 +540,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { - // @todo: assert problem properties + // TODO assert problem properties } } From 1ee5d994050f126dbd0ea05a7243cf1491e3d47f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:35:13 +0100 Subject: [PATCH 21/77] When changing watched literals of a rule, update the parent's next pointer The previous rule was not previously updated to point to the next rule when removing a middle rule from the watch tree for a literal. This resulted in jumping from one literal's watch tree to another's, which could then jump back to the original and cause infinite loop in a case like #265. Fixes #265 --- src/Composer/DependencyResolver/Solver.php | 19 ++++++++--- .../Test/DependencyResolver/SolverTest.php | 34 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 833548246..6d1293e1b 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1222,7 +1222,8 @@ class Solver 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); if ($rule->isDisabled()) { @@ -1242,13 +1243,23 @@ class Solver if ($otherWatch !== $ruleLiteral->getId() && !$this->decisionsConflict($ruleLiteral)) { - if ($literal->getId() === $rule->watch1) { $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 { $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->watch1 === $literal->getId()) { + $prevRule->next1 = $nextRule; + } else { + $prevRule->next2 = $nextRule; + } + } + else { + $this->watches[$literal->getId()] = $nextRule; } $this->watches[$ruleLiteral->getId()] = $rule; diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 18cb9f4a1..e74f11b06 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -485,6 +485,40 @@ 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', new VersionConstraint('>=', '2.0'), 'requires'), + new Link('C', 'D', new VersionConstraint('>=', '2.0'), 'requires'), + )); + + $packageD->setRequires(array( + new Link('D', 'A', new VersionConstraint('>=', '2.1'), 'requires'), + new Link('D', 'B', new VersionConstraint('>=', '2.0-dev'), 'requires'), + )); + + $packageB1->setRequires(array(new Link('B', 'A', new VersionConstraint('==', '2.1.0.0-dev'), 'requires'))); + $packageB2->setRequires(array(new Link('B', 'A', new VersionConstraint('==', '2.1.0.0-dev'), 'requires'))); + + $packageB2->setReplaces(array(new Link('B', 'D', new VersionConstraint('==', '2.0.9.0'), 'replaces'))); + + $this->reposComplete(); + + $this->request->install('C', new VersionConstraint('==', '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')); From f304a0f129d9159080e73850530356025b389ed1 Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Sun, 19 Feb 2012 15:38:03 +0100 Subject: [PATCH 22/77] Clean up the incomplete marker like suggested in https://github.com/composer/composer/pull/324#r465391 --- tests/Composer/Test/DependencyResolver/SolverTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 875dca41c..db7bfc1b6 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -57,6 +57,8 @@ class SolverTest extends TestCase public function testInstallNonExistingPackageFails() { + $this->markTestIncomplete('Reporting this failure is not implemented/working yet'); + $this->repo->addPackage($this->getPackage('A', '1.0')); $this->reposComplete(); @@ -64,8 +66,7 @@ class SolverTest extends TestCase try { $transaction = $this->solver->solve($this->request); - $this->markTestIncomplete('Reporting this failure is not implemented/working yet'); - //$this->fail('Unsolvable conflict did not resolve in exception.'); + $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { // TODO assert problem properties } From 2a92b904d2a5628fa52f27e1f71c6f7ad5aeed0d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:42:23 +0100 Subject: [PATCH 23/77] Correct placing of braces --- src/Composer/DependencyResolver/Solver.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 6d1293e1b..4ec591c29 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1257,8 +1257,7 @@ class Solver } else { $prevRule->next2 = $nextRule; } - } - else { + } else { $this->watches[$literal->getId()] = $nextRule; } @@ -2072,8 +2071,7 @@ class Solver if ($watch->next2 == $watch) { echo $indent." 2 *RECURSION*"; } - } - else if ($watch && ($watch->next1 || $watch->next2)) { + } elseif ($watch && ($watch->next1 || $watch->next2)) { $indent = str_replace(array('1', '2'), ' ', $indent); array_push($queue, array($indent.' 2 ', $watch->next2)); From af408eee9a5b480e385fd6dc5b452eabe78cd905 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:54:48 +0100 Subject: [PATCH 24/77] Fix line endings of Composer TestCase --- tests/Composer/Test/TestCase.php | 69 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index 17255ab2e..05a788517 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -1,34 +1,35 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Test; - -use Composer\Package\Version\VersionParser; -use Composer\Package\MemoryPackage; - -abstract class TestCase extends \PHPUnit_Framework_TestCase -{ - private static $versionParser; - - public static function setUpBeforeClass() - { - if (!self::$versionParser) { - self::$versionParser = new VersionParser(); - } - } - - protected function getPackage($name, $version) - { - $normVersion = self::$versionParser->normalize($version); - return new MemoryPackage($name, $normVersion, $version); - } -} \ No newline at end of file + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test; + +use Composer\Package\Version\VersionParser; +use Composer\Package\MemoryPackage; +use Composer\Package\LinkConstraint\VersionConstraint; + +abstract class TestCase extends \PHPUnit_Framework_TestCase +{ + private static $versionParser; + + public static function setUpBeforeClass() + { + if (!self::$versionParser) { + self::$versionParser = new VersionParser(); + } + } + + protected function getPackage($name, $version) + { + $normVersion = self::$versionParser->normalize($version); + return new MemoryPackage($name, $normVersion, $version); + } +} From 3b06a22e694edd6bab29c74d69a02eed838379a8 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 15:55:44 +0100 Subject: [PATCH 25/77] Add getVersionConstraint test case to avoid version normalization issues --- .../Test/DependencyResolver/SolverTest.php | 98 +++++++++---------- tests/Composer/Test/TestCase.php | 8 ++ 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index e74f11b06..b3c7f4a46 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -61,7 +61,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $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(); @@ -123,12 +123,12 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $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('B', new VersionConstraint('=', '1.1.0.0')); - $this->request->update('A', new VersionConstraint('=', '1.0.0.0')); - $this->request->update('B', new VersionConstraint('=', '1.0.0.0')); + $this->request->install('A', $this->getVersionConstraint('=', '1.0.0.0')); + $this->request->install('B', $this->getVersionConstraint('=', '1.1.0.0')); + $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0')); + $this->request->update('B', $this->getVersionConstraint('=', '1.0.0.0')); $this->checkSolverResult(array( array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB), @@ -182,7 +182,7 @@ class SolverTest extends TestCase $this->repo->addPackage($this->getPackage('A', '2.0')); $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->checkSolverResult(array(array( @@ -199,8 +199,8 @@ class SolverTest extends TestCase $this->repo->addPackage($this->getPackage('A', '2.0')); $this->reposComplete(); - $this->request->install('A', new VersionConstraint('<', '2.0.0.0')); - $this->request->update('A', new VersionConstraint('=', '1.0.0.0')); + $this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0')); + $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0')); $this->checkSolverResult(array(array( 'job' => 'update', @@ -217,8 +217,8 @@ class SolverTest extends TestCase $this->repo->addPackage($this->getPackage('A', '2.0')); $this->reposComplete(); - $this->request->install('A', new VersionConstraint('<', '2.0.0.0')); - $this->request->update('A', new VersionConstraint('=', '1.0.0.0')); + $this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0')); + $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0')); $this->checkSolverResult(array(array( 'job' => 'update', @@ -237,7 +237,7 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $this->repo->addPackage($packageC = $this->getPackage('C', '1.1')); $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(); @@ -259,8 +259,8 @@ class SolverTest extends TestCase $this->repo->addPackage($middlePackageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $this->repo->addPackage($oldPackageB = $this->getPackage('B', '0.9')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('<', '1.1'), 'requires'))); - $packageA->setConflicts(array(new Link('A', 'B', new VersionConstraint('<', '1.0'), 'conflicts'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); + $packageA->setConflicts(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.0'), 'conflicts'))); $this->reposComplete(); @@ -306,8 +306,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '0.8')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setProvides(array(new Link('Q', 'B', new VersionConstraint('=', '1.0'), 'provides'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setProvides(array(new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides'))); $this->reposComplete(); @@ -324,8 +324,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -341,8 +341,8 @@ class SolverTest extends TestCase { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -359,8 +359,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', new VersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -377,24 +377,24 @@ class SolverTest extends TestCase { $this->repo->addPackage($packageX = $this->getPackage('X', '1.0')); $packageX->setRequires(array( - new Link('X', 'A', new VersionConstraint('>=', '2.0.0.0'), 'requires'), - new Link('X', 'B', new VersionConstraint('>=', '2.0.0.0'), 'requires'))); + new Link('X', 'A', $this->getVersionConstraint('>=', '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($newPackageA = $this->getPackage('A', '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 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 // but an alternative option for A and B both exists // this creates a more difficult so solve conflict $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(); @@ -412,8 +412,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB1 = $this->getPackage('B', '0.9')); $this->repo->addPackage($packageB2 = $this->getPackage('B', '1.1')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageB2->setRequires(array(new Link('B', 'A', new VersionConstraint('>=', '1.0'), 'requires'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageB2->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'))); $this->reposComplete(); @@ -433,10 +433,10 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($packageC = $this->getPackage('C', '1.0')); $this->repo->addPackage($packageD = $this->getPackage('D', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageB->setRequires(array(new Link('B', 'Virtual', new VersionConstraint('>=', '1.0'), 'requires'))); - $packageC->setRequires(array(new Link('C', 'Virtual', new VersionConstraint('==', '1.0'), 'provides'))); - $packageD->setRequires(array(new Link('D', 'Virtual', new VersionConstraint('==', '1.0'), 'provides'))); + $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageB->setRequires(array(new Link('B', 'Virtual', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageC->setRequires(array(new Link('C', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); + $packageD->setRequires(array(new Link('D', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); $this->reposComplete(); @@ -461,18 +461,18 @@ class SolverTest extends TestCase $this->repo->addPackage($packageD2 = $this->getPackage('D', '1.1')); $packageA->setRequires(array( - new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'), - new Link('A', 'C', new VersionConstraint('>=', '1.0'), 'requires'), + new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'), + new Link('A', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageD->setReplaces(array( - new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'), - new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'), + new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), )); $packageD2->setReplaces(array( - new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'), - new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'), + new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), )); $this->reposComplete(); @@ -496,23 +496,23 @@ class SolverTest extends TestCase $this->repo->addPackage($packageD = $this->getPackage('D', '2.0.9')); $packageC->setRequires(array( - new Link('C', 'A', new VersionConstraint('>=', '2.0'), 'requires'), - new Link('C', 'D', new VersionConstraint('>=', '2.0'), 'requires'), + 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', new VersionConstraint('>=', '2.1'), 'requires'), - new Link('D', 'B', new VersionConstraint('>=', '2.0-dev'), 'requires'), + 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', new VersionConstraint('==', '2.1.0.0-dev'), 'requires'))); - $packageB2->setRequires(array(new Link('B', 'A', new VersionConstraint('==', '2.1.0.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', new VersionConstraint('==', '2.0.9.0'), 'replaces'))); + $packageB2->setReplaces(array(new Link('B', 'D', $this->getVersionConstraint('==', '2.0.9.0'), 'replaces'))); $this->reposComplete(); - $this->request->install('C', new VersionConstraint('==', '2.0.0.0-dev')); + $this->request->install('C', $this->getVersionConstraint('==', '2.0.0.0-dev')); $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); @@ -525,7 +525,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));; $packageA->setConflicts(array( - new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'conflicts'), + new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'conflicts'), )); $this->reposComplete(); @@ -547,7 +547,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $packageA->setRequires(array( - new Link('A', 'B', new VersionConstraint('>=', '2.0'), 'requires'), + new Link('A', 'B', $this->getVersionConstraint('>=', '2.0'), 'requires'), )); $this->reposComplete(); diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index 05a788517..1e3ae257e 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -27,6 +27,14 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase } } + protected function getVersionConstraint($operator, $version) + { + return new VersionConstraint( + $operator, + self::$versionParser->normalize($version) + ); + } + protected function getPackage($name, $version) { $normVersion = self::$versionParser->normalize($version); From 32157a39047a2cfd50bee69d571e5365a4728fc4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 16:38:07 +0100 Subject: [PATCH 26/77] Fix local git repo handling in GitDriver --- src/Composer/Repository/Vcs/GitDriver.php | 46 ++++++++++++++++++----- src/Composer/Repository/Vcs/VcsDriver.php | 5 +++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 8a8d1f8c6..e99a0e5f8 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -15,6 +15,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface protected $branches; protected $rootIdentifier; protected $infoCache = array(); + protected $isLocal = false; public function __construct($url, IOInterface $io, ProcessExecutor $process = null) { @@ -30,10 +31,15 @@ class GitDriver extends VcsDriver implements VcsDriverInterface { $url = escapeshellarg($this->url); $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 { - $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(); @@ -47,11 +53,27 @@ class GitDriver extends VcsDriver implements VcsDriverInterface { if (null === $this->rootIdentifier) { $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 ($branch && preg_match('{/HEAD +-> +[^/]+/(\S+)}', $branch, $match)) { - $this->rootIdentifier = $match[1]; - break; + + if ($this->isLocal) { + // select currently checked out branch if master is not available + $this->process->execute(sprintf('cd %s && git branch --no-color', escapeshellarg($this->tmpDir)), $output); + $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) { $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) { if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) { preg_match('{^ *[^/]+/(\S+) *([a-f0-9]+) .*$}', $branch, $match); @@ -170,7 +196,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface } // local filesystem - if (preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url)) { + if (static::isLocalUrl($url)) { $process = new ProcessExecutor(); // check whether there is a git repo in that path if ($process->execute(sprintf('cd %s && git show', escapeshellarg($url)), $output) === 0) { diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 7008f41be..75b631a42 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -68,4 +68,9 @@ abstract class VcsDriver $rfs = new RemoteFilesystem($this->io); return $rfs->getContents($this->url, $url, false); } + + protected static function isLocalUrl($url) + { + return (Boolean) preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url); + } } From a175ebed339efa7ab13164d24acee1da411cbfc2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 17:06:30 +0100 Subject: [PATCH 27/77] Add failing test for new dev behavior --- .../Test/Repository/VcsRepositoryTest.php | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/Composer/Test/Repository/VcsRepositoryTest.php diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php new file mode 100644 index 000000000..0071d3f7d --- /dev/null +++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php @@ -0,0 +1,140 @@ + + * Jordi Boggiano + * + * 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); + + // 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, + '0.9.0' => true, + '1.0.0' => 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))); + } + } + + if ($expected) { + $this->fail('Missing versions: '.implode(', ', $expected)); + } + + $this->pass(); + } +} From 882496b926288ba82ea7f732da76f825b96375c7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 17:43:12 +0100 Subject: [PATCH 28/77] Adjust version parser tests --- .../Test/Package/Version/VersionParserTest.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index faca00dc9..74e787bde 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -49,9 +49,10 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'parses datetime' => array('20100102-203040', '20100102-203040'), 'parses dt+number' => array('20100102203040-10', '20100102203040-10'), 'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'), - 'parses master' => array('master', '9999999-dev'), - 'parses trunk' => array('trunk', '9999999-dev'), - 'parses trunk/2' => array('trunk-dev', '9999999-dev'), + 'parses master' => array('dev-master', '9999999-dev'), + 'parses trunk' => array('dev-trunk', '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 type' => array('1.0.0-meh'), '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 master' => array('master', '9999999-dev'), 'parses trunk' => array('trunk', '9999999-dev'), + 'parses arbitrary' => array('feature-a', 'dev-feature-a'), + 'parses arbitrary/2' => array('foobar', 'dev-foobar'), ); } From ae7107fc223b0d82327d54c23241c4127a03fb39 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 19:00:40 +0100 Subject: [PATCH 29/77] Add more test requirements --- .../Test/Package/Version/VersionParserTest.php | 7 ++++--- tests/Composer/Test/Repository/VcsRepositoryTest.php | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 74e787bde..64a3e79f4 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -52,7 +52,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'parses master' => array('dev-master', '9999999-dev'), 'parses trunk' => array('dev-trunk', '9999999-dev'), 'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'), - 'parses arbitrary2' => array('dev-foobar', 'dev-foobar'), + 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-foobar'), ); } @@ -125,8 +125,9 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase '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')), 'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')), - 'accepts master' => array('>=master-dev', new VersionConstraint('>=', '9999999-dev')), - 'accepts master/2' => array('master-dev', new VersionConstraint('=', '9999999-dev')), + 'accepts master' => array('>=dev-master', 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')), ); } diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php index 0071d3f7d..4ea24725b 100644 --- a/tests/Composer/Test/Repository/VcsRepositoryTest.php +++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php @@ -86,6 +86,9 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase $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)); @@ -112,8 +115,9 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase { $expected = array( '0.6.0' => true, - '0.9.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, @@ -131,10 +135,6 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase } } - if ($expected) { - $this->fail('Missing versions: '.implode(', ', $expected)); - } - - $this->pass(); + $this->assertEmpty($expected, 'Missing versions: '.implode(', ', array_keys($expected))); } } From 3e6176eccf73ac0c89612b0243aa871a6a41299b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 19:08:44 +0100 Subject: [PATCH 30/77] Update version parser to support any branch name --- src/Composer/Package/Version/VersionParser.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 7bd941d93..716c24731 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -34,10 +34,15 @@ class VersionParser { $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'; } + if ('dev-' === strtolower(substr($version, 0, 4))) { + return strtolower($version); + } + // match classical versioning if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.$this->modifierRegex.'$}i', $version, $matches)) { $version = $matches[1] @@ -53,7 +58,7 @@ class VersionParser // add version modifiers if a version was matched if (isset($index)) { if (!empty($matches[$index])) { - $mod = array('{^pl?$}', '{^rc$}'); + $mod = array('{^pl?$}i', '{^rc$}i'); $modNormalized = array('patch', 'RC'); $version .= '-'.preg_replace($mod, $modNormalized, strtolower($matches[$index])) . (!empty($matches[$index+1]) ? $matches[$index+1] : ''); @@ -97,7 +102,7 @@ class VersionParser return str_replace('x', '9999999', $version).'-dev'; } - throw new \UnexpectedValueException('Invalid branch name '.$name); + return 'dev-'.$name; } /** From f73c08043f8b01628ad198a831091ccf02f612ba Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 19:26:43 +0100 Subject: [PATCH 31/77] Refactor VcsRepo to follow new dev model --- src/Composer/Repository/VcsRepository.php | 42 +++++++---------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index a2506d2df..115baad47 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -82,14 +82,10 @@ class VcsRepository extends ArrayRepository try { $data = $driver->getComposerInformation($identifier); } catch (\Exception $e) { - if (strpos($e->getMessage(), 'JSON Parse Error') !== false) { - if ($debug) { - $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); - } - continue; - } else { - throw $e; + if ($debug) { + $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); } + continue; } // manually versioned package @@ -103,7 +99,7 @@ class VcsRepository extends ArrayRepository // make sure tag packages have no -dev flag $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 if ($data['version_normalized'] !== $parsedTag) { @@ -131,34 +127,22 @@ class VcsRepository extends ArrayRepository if ($driver->hasComposerFile($identifier)) { $data = $driver->getComposerInformation($identifier); - // manually versioned package - 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 (!$parsedBranch) { if ($debug) { $this->io->write('Skipped branch '.$branch.', invalid name and no composer file was found'); } continue; } - // make sure branch packages have a -dev flag - $normalizedStableVersion = preg_replace('{[.-]?dev$}i', '', $data['version_normalized']); - $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']) . '-dev'; - $data['version_normalized'] = $normalizedStableVersion . '-dev'; + // branches are always auto-versionned, read value from branch name + $data['version'] = $branch; + $data['version_normalized'] = $parsedBranch; - // Skip branches that contain a version that has been tagged already - foreach ($this->getPackages() as $package) { - if ($normalizedStableVersion === $package->getVersion()) { - if ($debug) { - $this->io->write('Skipped branch '.$branch.', already tagged'); - } - - continue 2; - } + // make sure branch packages have a dev flag + if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { + $data['version'] = 'dev-' . $data['version']; + } else { + $data['version'] = $data['version'] . '-dev'; } if ($debug) { From 3fe87b1e35c60db74483f2c8acc4154dd9efbf5f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 23:21:45 +0100 Subject: [PATCH 32/77] Force dev packages to be installed from source --- src/Composer/Downloader/DownloadManager.php | 8 ++++---- src/Composer/Installer/LibraryInstaller.php | 4 +++- src/Composer/Package/MemoryPackage.php | 11 +++++++++++ src/Composer/Package/PackageInterface.php | 7 +++++++ .../Test/Installer/InstallerInstallerTest.php | 8 ++++---- .../Composer/Test/Installer/LibraryInstallerTest.php | 5 ++--- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index e466bbb09..e1441f182 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -125,14 +125,14 @@ class DownloadManager $sourceType = $package->getSourceType(); $distType = $package->getDistType(); - if (!($preferSource && $sourceType) && $distType) { + if (!$package->isDev() && !($preferSource && $sourceType) && $distType) { $package->setInstallationSource('dist'); } elseif ($sourceType) { $package->setInstallationSource('source'); + } elseif ($package->isDev()) { + throw new \InvalidArgumentException('Dev package '.$package.' must have a source specified'); } else { - throw new \InvalidArgumentException( - 'Package '.$package.' should have source or dist specified' - ); + throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } $fs = new Filesystem(); diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index a3f0b2b74..8eef0d7ed 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -109,7 +109,9 @@ class LibraryInstaller implements InstallerInterface $this->downloadManager->update($initial, $target, $downloadPath); $this->installBinaries($target); $this->repository->removePackage($initial); - $this->repository->addPackage(clone $target); + if (!$this->repository->hasPackage($target)) { + $this->repository->addPackage(clone $target); + } } /** diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index d1e63e102..a57f2b6f6 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -41,6 +41,7 @@ class MemoryPackage extends BasePackage protected $extra = array(); protected $binaries = array(); protected $scripts = array(); + protected $dev; protected $requires = array(); protected $conflicts = array(); @@ -63,6 +64,16 @@ class MemoryPackage extends BasePackage $this->version = $version; $this->prettyVersion = $prettyVersion; + + $this->dev = 'dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4); + } + + /** + * {@inheritDoc} + */ + public function isDev() + { + return $this->dev; } /** diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index c8f92b581..1f19a835f 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -68,6 +68,13 @@ interface PackageInterface */ 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 * diff --git a/tests/Composer/Test/Installer/InstallerInstallerTest.php b/tests/Composer/Test/Installer/InstallerInstallerTest.php index 4e2f8c732..eab2d948a 100644 --- a/tests/Composer/Test/Installer/InstallerInstallerTest.php +++ b/tests/Composer/Test/Installer/InstallerInstallerTest.php @@ -67,9 +67,9 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->method('getPackages') ->will($this->returnValue(array($this->packages[0]))); $this->repository - ->expects($this->once()) + ->expects($this->exactly(2)) ->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); $test = $this; @@ -90,9 +90,9 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->method('getPackages') ->will($this->returnValue(array($this->packages[1]))); $this->repository - ->expects($this->once()) + ->expects($this->exactly(2)) ->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); $test = $this; diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index 3399345f0..ba86954e3 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -128,10 +128,9 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('package1')); $this->repository - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('hasPackage') - ->with($initial) - ->will($this->onConsecutiveCalls(true, false)); + ->will($this->onConsecutiveCalls(true, false, false)); $this->dm ->expects($this->once()) From c1baa20feca5f645c420bf91094e3dcb9705d3d4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Feb 2012 23:48:12 +0100 Subject: [PATCH 33/77] Lock reference of dev packages --- src/Composer/Package/Locker.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 21e09eaf3..09a187313 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -116,7 +116,13 @@ class Locker )); } - $lock['packages'][] = array('package' => $name, 'version' => $version); + $spec = array('package' => $name, 'version' => $version); + + if ($package->isDev()) { + $spec['reference'] = $package->getSourceReference(); + } + + $lock['packages'][] = $spec; } $this->lockFile->write($lock); From 496188f71415ddd87a144f7752eeeff45d2e4799 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 00:13:51 +0100 Subject: [PATCH 34/77] Force installs from lock to reinstall dev packages from the exact locked reference --- src/Composer/Command/InstallCommand.php | 20 ++++++++++++++++++++ src/Composer/Package/Locker.php | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d656ad5d1..86552362b 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -113,6 +113,7 @@ EOT } // creating requirements request + $installFromLock = false; $request = new Request($pool); if ($update) { $io->write('Updating dependencies'); @@ -130,6 +131,7 @@ EOT $request->install($link->getTarget(), $link->getConstraint()); } } elseif ($composer->getLocker()->isLocked()) { + $installFromLock = true; $io->write('Installing from lock file'); if (!$composer->getLocker()->isFresh()) { @@ -196,7 +198,25 @@ EOT } if (!$dryRun) { $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()) { + foreach ($composer->getLocker()->getLockedPackages() as $lockedPackage) { + if (!empty($lockedPackage['source_reference']) && strtolower($lockedPackage['package']) === $package->getName()) { + $package->setSourceReference($lockedPackage['source_reference']); + } + } + } + } $installationManager->execute($operation); + $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); } } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 09a187313..39a299873 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -119,7 +119,7 @@ class Locker $spec = array('package' => $name, 'version' => $version); if ($package->isDev()) { - $spec['reference'] = $package->getSourceReference(); + $spec['source_reference'] = $package->getSourceReference(); } $lock['packages'][] = $spec; From 88b018068cdcde719489a4e59d0bb223ee88f4d4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 00:51:13 +0100 Subject: [PATCH 35/77] Force dev packages to update to latest ref on update --- src/Composer/Command/InstallCommand.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 86552362b..e2dcb6a38 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -30,6 +30,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Solver; use Composer\IO\IOInterface; @@ -192,6 +193,28 @@ EOT if (!$operations) { $io->write('Nothing to install/update'); } + + // force dev packages to be updated to latest reference on update + if ($update) { + foreach ($installedPackages as $package) { + if (!$package->isDev()) { + continue; + } + 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()); + $operation = new UpdateOperation($package, $newPackage); + $operations[] = $operation; + } + } + foreach ($operations as $operation) { if ($verbose) { $io->write((string) $operation); From d7350b66d52c228ed6151ee771e0ca17e0f7792e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 12:05:09 +0100 Subject: [PATCH 36/77] Ensure dev packages have a source --- src/Composer/Package/Loader/ArrayLoader.php | 2 ++ src/Composer/Package/Loader/RootPackageLoader.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 6d6f4f15d..7c992392c 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -122,6 +122,8 @@ class ArrayLoader $package->setSourceType($config['source']['type']); $package->setSourceUrl($config['source']['url']); $package->setSourceReference($config['source']['reference']); + } elseif ($package->isDev()) { + throw new \UnexpectedValueException('Dev package '.$package.' must have a source specified'); } if (isset($config['dist'])) { diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 294dd3f42..208bc4fcf 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -38,7 +38,7 @@ class RootPackageLoader extends ArrayLoader $config['name'] = '__root__'; } if (!isset($config['version'])) { - $config['version'] = '1.0.0-dev'; + $config['version'] = '1.0.0'; } $package = parent::load($config); From c12dccd3d42f998b99d79d754b240dda51bf7029 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 13:48:42 +0100 Subject: [PATCH 37/77] Restore quality of debug output --- src/Composer/Repository/VcsRepository.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 115baad47..2c4175fa0 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -76,7 +76,13 @@ class VcsRepository extends ArrayRepository } foreach ($driver->getTags() as $tag => $identifier) { - $this->io->overwrite('Get composer of ' . $this->packageName . ' (' . $tag . ')', false); + $msg = 'Get composer info for ' . $this->packageName . ' (' . $tag . ')'; + if ($debug) { + $this->io->write($msg); + } else { + $this->io->overwrite($msg, false); + } + $parsedTag = $this->validateTag($versionParser, $tag); if ($parsedTag && $driver->hasComposerFile($identifier)) { try { @@ -122,7 +128,13 @@ class VcsRepository extends ArrayRepository $this->io->overwrite('', false); foreach ($driver->getBranches() as $branch => $identifier) { - $this->io->overwrite('Get composer of ' . $this->packageName . ' (' . $branch . ')', false); + $msg = 'Get composer info for ' . $this->packageName . ' (' . $branch . ')'; + if ($debug) { + $this->io->write($msg); + } else { + $this->io->overwrite($msg, false); + } + $parsedBranch = $this->validateBranch($versionParser, $branch); if ($driver->hasComposerFile($identifier)) { $data = $driver->getComposerInformation($identifier); From 646d01658a8aa7dfcc467e8be4197045bc429747 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 15:20:03 +0100 Subject: [PATCH 38/77] Rename source_ref to source-ref --- src/Composer/Command/InstallCommand.php | 4 ++-- src/Composer/Package/Locker.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index e2dcb6a38..937393a1c 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -232,8 +232,8 @@ EOT } if ($package && $package->isDev()) { foreach ($composer->getLocker()->getLockedPackages() as $lockedPackage) { - if (!empty($lockedPackage['source_reference']) && strtolower($lockedPackage['package']) === $package->getName()) { - $package->setSourceReference($lockedPackage['source_reference']); + if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) { + $package->setSourceReference($lockedPackage['source-reference']); } } } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 39a299873..255835ec3 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -119,7 +119,7 @@ class Locker $spec = array('package' => $name, 'version' => $version); if ($package->isDev()) { - $spec['source_reference'] = $package->getSourceReference(); + $spec['source-reference'] = $package->getSourceReference(); } $lock['packages'][] = $spec; From 4233a4823d7316e6d363c5bd0daad066d2a6cd73 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 16:33:19 +0100 Subject: [PATCH 39/77] Skip platform repo and only force-update packages that have a new source ref --- src/Composer/Command/InstallCommand.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 937393a1c..760c14e8f 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -196,10 +196,13 @@ EOT // force dev packages to be updated to latest reference on update if ($update) { - foreach ($installedPackages as $package) { + 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()) @@ -210,8 +213,10 @@ EOT // force update $newPackage = $composer->getRepositoryManager()->findPackage($package->getName(), $package->getVersion()); - $operation = new UpdateOperation($package, $newPackage); - $operations[] = $operation; + if ($newPackage->getSourceReference() !== $package->getSourceReference()) { + $operation = new UpdateOperation($package, $newPackage); + $operations[] = $operation; + } } } From c95b4d05fdf45c8d48268fa64dc59f63e9ada27b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 16:34:35 +0100 Subject: [PATCH 40/77] Fix forcing of the source-ref from lock file --- src/Composer/Command/InstallCommand.php | 4 +++- src/Composer/Package/Locker.php | 15 ++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 760c14e8f..5fe2e64db 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -236,9 +236,11 @@ EOT $package = $operation->getPackage(); } if ($package && $package->isDev()) { - foreach ($composer->getLocker()->getLockedPackages() as $lockedPackage) { + $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; } } } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 255835ec3..24aafae8f 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -69,11 +69,7 @@ class Locker */ public function getLockedPackages() { - if (!$this->isLocked()) { - throw new \LogicException('No lockfile found. Unable to read locked packages'); - } - - $lockList = $this->lockFile->read(); + $lockList = $this->getLockData(); $packages = array(); foreach ($lockList['packages'] as $info) { $package = $this->repositoryManager->getLocalRepository()->findPackage($info['package'], $info['version']); @@ -95,6 +91,15 @@ class Locker 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. * From 3e1749fbad5df859ad0a8a31abfed0a103f92c05 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 16:59:04 +0100 Subject: [PATCH 41/77] Readd update-all jobs and make them available through the request --- src/Composer/DependencyResolver/Request.php | 5 +++++ src/Composer/DependencyResolver/Solver.php | 8 ++++++++ .../Test/DependencyResolver/RequestTest.php | 12 +++++++++++ .../Test/DependencyResolver/SolverTest.php | 20 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/src/Composer/DependencyResolver/Request.php b/src/Composer/DependencyResolver/Request.php index 201caa1d3..3d1b28448 100644 --- a/src/Composer/DependencyResolver/Request.php +++ b/src/Composer/DependencyResolver/Request.php @@ -55,6 +55,11 @@ class Request ); } + public function updateAll() + { + $this->jobs[] = array('cmd' => 'update-all', 'packages' => array()); + } + public function getJobs() { return $this->jobs; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index b5bc5cac2..1268a0040 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -952,6 +952,14 @@ class Solver break; } } + + switch ($job['cmd']) { + case 'update-all': + foreach ($installedPackages as $package) { + $this->updateMap[$package->getId()] = true; + } + break; + } } foreach ($installedPackages as $package) { diff --git a/tests/Composer/Test/DependencyResolver/RequestTest.php b/tests/Composer/Test/DependencyResolver/RequestTest.php index da186edb2..e5010e0e4 100644 --- a/tests/Composer/Test/DependencyResolver/RequestTest.php +++ b/tests/Composer/Test/DependencyResolver/RequestTest.php @@ -46,4 +46,16 @@ class RequestTest extends TestCase ), $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()); + } } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index e913dc2a8..28ab91851 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -165,6 +165,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() { $this->repoInstalled->addPackage($this->getPackage('A', '1.0')); From 509188c112b9117cb7ab30d1a9e03dd4d69f28ce Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 17:14:11 +0100 Subject: [PATCH 42/77] Use updateAll request method in upddate mode in install command --- src/Composer/Command/InstallCommand.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d656ad5d1..d6f60028b 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -119,14 +119,9 @@ EOT $installedPackages = $installedRepo->getPackages(); $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); - foreach ($links as $link) { - foreach ($installedPackages as $package) { - if ($package->getName() === $link->getTarget()) { - $request->update($package->getName(), new VersionConstraint('=', $package->getVersion())); - break; - } - } + $request->updateAll(); + foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } } elseif ($composer->getLocker()->isLocked()) { From ab51095f4ca46093b225a03eed43be2bb8210c36 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 18:11:21 +0100 Subject: [PATCH 43/77] Add scripts to json schema --- doc/composer-schema.json | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/doc/composer-schema.json b/doc/composer-schema.json index 2545a8d4b..44ac9e6e7 100644 --- a/doc/composer-schema.json +++ b/doc/composer-schema.json @@ -121,6 +121,52 @@ "type": ["object", "array"], "description": "A set of additional repositories where packages can be found.", "additionalProperties": true + }, + "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.", + } + } } } } From 0cb02cf3cae61363c6162cc4d8c338bc9269740f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 18:24:27 +0100 Subject: [PATCH 44/77] Add config in composer json schema --- doc/composer-schema.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/composer-schema.json b/doc/composer-schema.json index 44ac9e6e7..22311105d 100644 --- a/doc/composer-schema.json +++ b/doc/composer-schema.json @@ -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).", "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": { "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.", From 44e76e06fcbe905bf22784b54848584c1a37de0c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 19:00:12 +0100 Subject: [PATCH 45/77] Refactor repositories configuration --- src/Composer/Factory.php | 9 ++++++++- src/Composer/Package/Loader/RootPackageLoader.php | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e4c81e824..2bbbded74 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -51,6 +51,7 @@ class Factory // Configuration defaults $composerConfig = array( 'vendor-dir' => 'vendor', + 'repositories' => array(), ); $packageConfig = $file->read(); @@ -71,7 +72,13 @@ class Factory $rm = $this->createRepositoryManager($io); // load default repository unless it's explicitly disabled - if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) { + $loadPackagist = true; + foreach ($packageConfig['repositories'] as $repo) { + if (isset($repo['packagist']) && $repo['packagist'] === false) { + $loadPackagist = false; + } + } + if ($loadPackagist) { $this->addPackagistRepository($rm); } diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 294dd3f42..bd0848a0e 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -44,14 +44,17 @@ class RootPackageLoader extends ArrayLoader $package = parent::load($config); if (isset($config['repositories'])) { - foreach ($config['repositories'] as $repoName => $repo) { - if (false === $repo && 'packagist' === $repoName) { + foreach ($config['repositories'] as $index => $repo) { + if (isset($repo['packagist']) && $repo['packagist'] === false) { continue; } 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); } $package->setRepositories($config['repositories']); From d0d5329f6fae5d520b290cd3b3554f0705492329 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Feb 2012 19:12:56 +0100 Subject: [PATCH 46/77] Fix package repositories --- src/Composer/Repository/PackageRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/PackageRepository.php b/src/Composer/Repository/PackageRepository.php index 01b1b24c2..fe2e83187 100644 --- a/src/Composer/Repository/PackageRepository.php +++ b/src/Composer/Repository/PackageRepository.php @@ -33,7 +33,7 @@ class PackageRepository extends ArrayRepository */ public function __construct(array $config) { - $this->config = $config; + $this->config = $config['package']; } /** From ff620afe0585e9ad38ea5f74b50b18656308ce71 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 18:57:03 +0100 Subject: [PATCH 47/77] Correct the parent path in the watch tree, after moving a rule out of the path --- src/Composer/DependencyResolver/Solver.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 1268a0040..e1d6575cd 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1266,7 +1266,7 @@ class Solver } if ($prevRule) { - if ($prevRule->watch1 === $literal->getId()) { + if ($prevRule->next1 == $rule) { $prevRule->next1 = $nextRule; } else { $prevRule->next2 = $nextRule; @@ -1276,6 +1276,8 @@ class Solver } $this->watches[$ruleLiteral->getId()] = $rule; + + $rule = $prevRule; continue 2; } } @@ -1506,7 +1508,7 @@ class Solver } $why = count($this->learnedPool) - 1; - + assert($learnedLiterals[0] !== null); $newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why); return array($ruleLevel, $newRule, $why); From dd443cd570d89a6d21456be6dd98b857843008e1 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 20:08:15 +0100 Subject: [PATCH 48/77] Complete circular dependency with alternatives through virtual packages test --- tests/Composer/Test/DependencyResolver/SolverTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 28ab91851..70509ea0f 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -464,16 +464,17 @@ class SolverTest extends TestCase public function testInstallAlternativeWithCircularRequire() { - $this->markTestIncomplete(); - $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($packageC = $this->getPackage('C', '1.0')); $this->repo->addPackage($packageD = $this->getPackage('D', '1.0')); $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); $packageB->setRequires(array(new Link('B', 'Virtual', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageC->setRequires(array(new Link('C', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); - $packageD->setRequires(array(new Link('D', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); + $packageC->setProvides(array(new Link('C', 'Virtual', $this->getVersionConstraint('==', '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(); From f4511271a86f775e0c4aed7e52bfd3103c10933b Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 21:07:30 +0100 Subject: [PATCH 49/77] [docs] schema chapter describing composer.json --- Resources/composer-schema.json | 7 + doc/04-schema.md | 349 +++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 doc/04-schema.md diff --git a/Resources/composer-schema.json b/Resources/composer-schema.json index 2545a8d4b..70b8bff13 100644 --- a/Resources/composer-schema.json +++ b/Resources/composer-schema.json @@ -121,6 +121,13 @@ "type": ["object", "array"], "description": "A set of additional repositories where packages can be found.", "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" + } } } } diff --git a/doc/04-schema.md b/doc/04-schema.md new file mode 100644 index 000000000..342b562f1 --- /dev/null +++ b/doc/04-schema.md @@ -0,0 +1,349 @@ +# 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/docs/Resources/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. + +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": "trunk" + } + } + } + ] +} +``` + +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 [faq/bin.md] for more details. + +Optional. From 2df96bcad35f6bb52ab5f4d4887751ee961e24f8 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 21:15:51 +0100 Subject: [PATCH 50/77] [schema] Fix invalid JSON --- Resources/composer-schema.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Resources/composer-schema.json b/Resources/composer-schema.json index e4b9c4162..62d6be0ed 100644 --- a/Resources/composer-schema.json +++ b/Resources/composer-schema.json @@ -149,43 +149,43 @@ "properties": { "pre-install-cmd": { "type": ["array", "string"], - "description": "Occurs before the install command is executed, contains one or more Class::method callables.", + "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.", + "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.", + "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.", + "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.", + "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.", + "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.", + "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.", + "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.", + "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.", + "description": "Occurs after a package has been uninstalled, contains one or more Class::method callables." } } } From b6079f73bad4968f0d29239f63e885c65f970238 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 22:31:04 +0100 Subject: [PATCH 51/77] [docs] beginning of the repositories chapter --- doc/04-schema.md | 2 ++ doc/05-repositories.md | 77 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 doc/05-repositories.md diff --git a/doc/04-schema.md b/doc/04-schema.md index 342b562f1..1d576c2ff 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -204,6 +204,8 @@ 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. diff --git a/doc/05-repositories.md b/doc/05-repositories.md new file mode 100644 index 000000000..3a3e56fc7 --- /dev/null +++ b/doc/05-repositories.md @@ -0,0 +1,77 @@ +# 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` is the `composer.json` of 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 + +### PEAR + +### Package + +## Hosting your own \ No newline at end of file From 31a75f06daab1fbb4b3439dd9df043bd981baeef Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 22:58:36 +0100 Subject: [PATCH 52/77] [docs] put notes into quote blocks to highlight them --- doc/01-basic-usage.md | 5 ++--- doc/02-libraries.md | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 13180bca8..3fcb71fa9 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -24,8 +24,7 @@ To check if composer is working, just run the PHAR through `php`: 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`. +> **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 @@ -165,4 +164,4 @@ $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. +> **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. diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 53366c45c..28b1dc2e8 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -19,7 +19,7 @@ In order to make that package installable you need to give it a name. You do thi 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. +> **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 @@ -48,7 +48,7 @@ Here are a few examples of valid tag names: 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. +> **Note:** If you specify an explicit version in `composer.json`, the tag name must match the specified version. ### Branches @@ -61,7 +61,7 @@ Here are some examples of version branch names: 1.1.x 1.1.* -**Note:** When you install a dev version, it will install it from source. See [Repositories] for more information. +> **Note:** When you install a dev version, it will install it from source. See [Repositories] for more information. ## Lock file From c8f3fa5800e49dfbc5667ce863db9a1c228578d8 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 19 Feb 2012 23:01:05 +0100 Subject: [PATCH 53/77] [docs] add cli sample to quote block --- doc/01-basic-usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index 3fcb71fa9..d483daa51 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -25,8 +25,8 @@ To check if composer is working, just run the PHAR through `php`: 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 +> +> $ curl -s http://getcomposer.org/installer | php -- --help ## Project setup From cabe1c2949777f9b25e772ad2f618909550531f2 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 01:04:35 +0100 Subject: [PATCH 54/77] [docs] add vcs repo section to repos chapter --- doc/04-schema.md | 2 ++ doc/05-repositories.md | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 1d576c2ff..b48c5f798 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -252,6 +252,8 @@ Example: } ``` +> **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 diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 3a3e56fc7..3fb20f3a7 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -45,7 +45,7 @@ The main repository type is the `composer` repository. It uses a single `package } ``` -The `@composer.json` is the `composer.json` of that package version including as a minimum: +The `@composer.json` marker would be the contents of the `composer.json` from that package version including as a minimum: * name * version @@ -70,8 +70,43 @@ The `composer` repository is also what packagist uses. To reference a `composer` ### 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 ### Package -## Hosting your own \ No newline at end of file +## Hosting your own From 2976bd82b05905965676e4eb02fa47050467f252 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 09:50:02 +0100 Subject: [PATCH 55/77] Simplify code --- src/Composer/Command/InstallCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 5fe2e64db..60250d0d0 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -214,8 +214,7 @@ EOT // force update $newPackage = $composer->getRepositoryManager()->findPackage($package->getName(), $package->getVersion()); if ($newPackage->getSourceReference() !== $package->getSourceReference()) { - $operation = new UpdateOperation($package, $newPackage); - $operations[] = $operation; + $operations[] = new UpdateOperation($package, $newPackage); } } } From c42e37037a3de7ab2ba0c4d30a55cd4f476f7ef1 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 11:08:18 +0100 Subject: [PATCH 56/77] [docs] wrap all lines to 80 chars --- doc/01-basic-usage.md | 58 +++++++++++------ doc/02-libraries.md | 89 +++++++++++++++++++------- doc/03-cli.md | 50 ++++++++++----- doc/04-schema.md | 142 ++++++++++++++++++++++++++++++----------- doc/05-repositories.md | 77 ++++++++++++++++------ 5 files changed, 304 insertions(+), 112 deletions(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index d483daa51..5c17048e9 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -24,13 +24,15 @@ To check if composer is working, just run the PHAR through `php`: 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`. +> **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 +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 @@ -74,10 +76,11 @@ Version constraints can be specified in a few different ways. 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`. + 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`. +* **Wildcard:** You can specify a pattern with a `*` wildcard. `1.0.*` is the + equivalent of `>=1.0,<1.1-dev`. ## Installing dependencies @@ -95,8 +98,8 @@ In case of monolog it will put it into `vendor/monolog/monolog`. `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. +Another thing that the `install` command does is it adds a `composer.lock` +file into your project root. ## Lock file @@ -119,22 +122,33 @@ also update the lock file. ## 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. +[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. +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. +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. +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. +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'); @@ -143,7 +157,8 @@ $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`. +You can even add your own code to the autoloader by adding an `autoload` key +to `composer.json`. ```json { @@ -153,15 +168,22 @@ You can even add your own code to the autoloader by adding an `autoload` key to } ``` -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. +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. +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. +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. +> **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. diff --git a/doc/02-libraries.md b/doc/02-libraries.md index 28b1dc2e8..adfe88c91 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -4,9 +4,13 @@ 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. +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`: +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 { @@ -17,13 +21,18 @@ In order to make that package installable you need to give it a name. You do thi } ``` -In this case the project name is `acme/hello-world`, where `acme` is the vendor name. Supplying a vendor name is mandatory. +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. +> **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. +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: @@ -33,11 +42,14 @@ If you do want to specify it explicitly, you can just add a `version` field: } ``` -However if you are using git, svn or hg, you don't have to specify it. Composer will detect versions as follows: +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. +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: @@ -52,7 +64,11 @@ Here are a few examples of valid tag names: ### 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. +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: @@ -61,11 +77,16 @@ Here are some examples of version branch names: 1.1.x 1.1.* -> **Note:** When you install a dev version, it will install it from source. See [Repositories] for more information. +> **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. +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.** @@ -73,9 +94,16 @@ 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`. +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`: +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 { @@ -86,9 +114,13 @@ Now, To test installing the `acme/hello-world` package, we create a new project } ``` -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. +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`: +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 { @@ -104,20 +136,33 @@ Now we need to tell the blog app where to find the `hello-world` dependency. We } ``` -For more details on how package repositories work and what other types are available, see [Repositories]. +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! +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. +**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. +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. +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 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. +Packagist 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. +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, at which point packagist will start crawling it. Once it is done, your package will be available to anyone. +You simply hit the big "Submit Package" button and sign up. Then you submit +the URL to your VCS, at which point packagist will start crawling it. Once it +is done, your package will be available to anyone. diff --git a/doc/03-cli.md b/doc/03-cli.md index 56989c466..99b507deb 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -1,24 +1,32 @@ # 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. +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. +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. +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`. +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 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. +If there is no `composer.lock` file, composer will create one after dependency +resolution. ### Options @@ -29,11 +37,13 @@ If there is no `composer.lock` file, composer will create one after dependency r ## update -In order to get the latest versions of the dependencies and to update the `composer.lock` file, you should use the `update` command. +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`. +This will resolve all dependencies of the project and write the exact versions +into `composer.lock`. ### Options @@ -44,7 +54,9 @@ This will resolve all dependencies of the project and write the exact versions i ## 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. +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 @@ -56,7 +68,8 @@ 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. +If you want to see the details of a certain package, you can pass the package +name. $ php composer.phar show monolog/monolog @@ -75,7 +88,8 @@ If you want to see the details of a certain package, you can pass the package na requires php >=5.3.0 -You can even pass the package version, which will tell you the details of that specific version. +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 @@ -86,7 +100,9 @@ You can even pass the package version, which will tell you the details of that s ## 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. +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 @@ -98,17 +114,21 @@ The `depends` command tells you which other packages depend on a certain package ### Options -* **--link-type:** The link types to match on, can be specified multiple times. +* **--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. +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. +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 diff --git a/doc/04-schema.md b/doc/04-schema.md index b48c5f798..2c9383ee3 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -4,7 +4,11 @@ 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/docs/Resources/composer-schema.json). +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/docs/Resources +/composer-schema.json). ## Package root @@ -12,7 +16,8 @@ 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 `/`. +The name of the package. It consists of vendor name and project name, +separated by `/`. Examples: @@ -31,7 +36,8 @@ Optional but recommended. The version of the package. -This must follow the format of `X.Y.Z` with an optional suffix of `-dev`, `alphaN`, `-betaN` or `-RCN`. +This must follow the format of `X.Y.Z` with an optional suffix of `-dev`, +`alphaN`, `-betaN` or `-RCN`. Examples: @@ -43,24 +49,36 @@ Examples: 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. +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. +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. +* **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`. +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. +An array of keywords that the package is related to. These can be used for +searching and filtering. Examples: @@ -142,10 +160,17 @@ 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`. +* **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: @@ -163,7 +188,10 @@ Optional. 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. +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: @@ -181,9 +209,15 @@ Optional, but it is highly recommended that you follow PSR-0 and use this. Defines the installation target. -In case the package root is below the namespace declaration you cannot autoload properly. `target-dir` solves this problem. +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`. +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: @@ -202,16 +236,26 @@ Optional. Custom package repositories to use. -By default composer just uses the packagist repository. By specifying repositories you can get packages from elsewhere. +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. +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. +* **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]. @@ -252,9 +296,12 @@ Example: } ``` -> **Note:** Order is significant here. Repositories added later will take precedence. This also means that custom repositories can override packages that exist on packagist. +> **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`. +You can also disable the packagist repository by setting `packagist` to +`false`. ```json { @@ -272,8 +319,10 @@ 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. +* **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: @@ -291,18 +340,29 @@ Composer allows you to hook into various parts of the installation process throu 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. +* **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. +For each of these events you can provide a static method on a class that will +handle it. Example: @@ -316,7 +376,9 @@ Example: } ``` -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. +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; @@ -336,7 +398,8 @@ class ScriptHandler Arbitrary extra data for consumption by `scripts`. -This can be virtually anything. To access it from within a script event handler, you can do: +This can be virtually anything. To access it from within a script event +handler, you can do: ```php $extra = $event->getComposer()->getPackage()->getExtra(); @@ -346,7 +409,8 @@ Optional. ## bin -A set of files that should be treated as binaries and symlinked into the `bin-dir` (from config). +A set of files that should be treated as binaries and symlinked into the `bin- +dir` (from config). See [faq/bin.md] for more details. diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 3fb20f3a7..6c465cdff 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -1,36 +1,56 @@ # Repositories -This chapter will explain the concept of packages and repositories, what kinds of repositories are available, and how they work. +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. +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. +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 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. +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. +**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. +**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. +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. +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`. +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: +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 { @@ -45,7 +65,8 @@ The main repository type is the `composer` repository. It uses a single `package } ``` -The `@composer.json` marker would be the contents of the `composer.json` from that package version including as a minimum: +The `@composer.json` marker would be the contents of the `composer.json` from +that package version including as a minimum: * name * version @@ -66,13 +87,27 @@ Here is a minimal package definition: 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`. +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. +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. +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: @@ -90,15 +125,21 @@ Example assuming you patched monolog to fix a bug in the `bugfix` branch: } ``` -When you run `php composer.phar update`, you should get your modified version of `monolog/monolog` instead of the one from packagist. +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 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. +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) From 005559dc2fd37a97771d3e11ccc4be19c8b2f2c9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 11:53:58 +0100 Subject: [PATCH 57/77] Fix composer dependencies --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 3531ba4ec..7b89586b2 100644 --- a/composer.json +++ b/composer.json @@ -19,9 +19,9 @@ ], "require": { "php": ">=5.3.0", - "symfony/console": "2.1.0-dev", - "symfony/finder": ">2.0,<2.2-dev", - "symfony/process": ">2.0,<2.2-dev" + "symfony/console": "dev-master", + "symfony/finder": "dev-master", + "symfony/process": "dev-master" }, "recommend": { "ext-zip": "*" From eb3524423024b71e125ab8d9a7cff41f8352ff2b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 11:55:32 +0100 Subject: [PATCH 58/77] Fix repositories warning --- src/Composer/Factory.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 2bbbded74..22bae3667 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -51,7 +51,6 @@ class Factory // Configuration defaults $composerConfig = array( 'vendor-dir' => 'vendor', - 'repositories' => array(), ); $packageConfig = $file->read(); @@ -73,9 +72,11 @@ class Factory // load default repository unless it's explicitly disabled $loadPackagist = true; - foreach ($packageConfig['repositories'] as $repo) { - if (isset($repo['packagist']) && $repo['packagist'] === false) { - $loadPackagist = false; + if (isset($packageConfig['repositories'])) { + foreach ($packageConfig['repositories'] as $repo) { + if (isset($repo['packagist']) && $repo['packagist'] === false) { + $loadPackagist = false; + } } } if ($loadPackagist) { From 621388e3bed00363beb57b96990e4da69bb7236f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 12:14:44 +0100 Subject: [PATCH 59/77] Fix warning when no package to update is found --- src/Composer/Command/InstallCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 41ab72a5c..fb7531139 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -208,7 +208,7 @@ EOT // force update $newPackage = $composer->getRepositoryManager()->findPackage($package->getName(), $package->getVersion()); - if ($newPackage->getSourceReference() !== $package->getSourceReference()) { + if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { $operations[] = new UpdateOperation($package, $newPackage); } } From de1765aa486673bb6c55d414c31b1edbc5841c56 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 20 Feb 2012 11:10:36 +0100 Subject: [PATCH 60/77] Remove workaround error detection in install command --- src/Composer/Command/InstallCommand.php | 28 ------------------------- 1 file changed, 28 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index fb7531139..5885b033d 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -156,34 +156,6 @@ EOT // solve dependencies $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 if (!$operations) { $io->write('Nothing to install/update'); From 8bdf4d8bcde4293bac9062c3dadc16dc5f776c50 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 12:34:28 +0100 Subject: [PATCH 61/77] Update lock file --- composer.lock | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index da880d14a..4761b3466 100644 --- a/composer.lock +++ b/composer.lock @@ -1,17 +1,20 @@ { - "hash": "9c243b2c15fdc7c3e35c5200d704ba53", + "hash": "4ba2fad397e186b6bc453b4417c2ab00", "packages": [ { - "package": "symfony\/process", - "version": "2.1.0-dev" + "package": "symfony/console", + "version": "dev-master", + "source-reference": "75ca31776bd98ad427f759cbe8a62400e40c73a1" }, { - "package": "symfony\/finder", - "version": "2.1.0-dev" + "package": "symfony/finder", + "version": "dev-master", + "source-reference": "dd56fc9f1f0baa006d7491d5c17eb3e2dd8a066c" }, { - "package": "symfony\/console", - "version": "2.1.0-dev" + "package": "symfony/process", + "version": "dev-master", + "source-reference": "f381eeee3733ca0fd374491fab56dce0f3ca8e34" } ] -} \ No newline at end of file +} From 9ba05057517fcf01b30d97f085ede38d0c13d27a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 12:35:03 +0100 Subject: [PATCH 62/77] Make sure lock file is sorted, fixes #276 --- src/Composer/Package/Locker.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 24aafae8f..c55c0e467 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -129,6 +129,9 @@ class Locker $lock['packages'][] = $spec; } + usort($lock['packages'], function ($a, $b) { + return strcmp($a['package'], $b['package']); + }); $this->lockFile->write($lock); } From 06f1ecf83a6a33585abecdc4c6a8df008bd8f8b5 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 12:51:30 +0100 Subject: [PATCH 63/77] [pear-prefix] add a pear-{channel}/ prefix to PearRepository packages --- src/Composer/Repository/PearRepository.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index edf5b9e3b..71f8bd8a9 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -22,6 +22,7 @@ use Composer\Util\StreamContextFactory; class PearRepository extends ArrayRepository { private $url; + private $channel; private $streamContext; public function __construct(array $config) @@ -29,11 +30,14 @@ class PearRepository extends ArrayRepository if (!preg_match('{^https?://}', $config['url'])) { $config['url'] = 'http://'.$config['url']; } + if (!filter_var($config['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']); } $this->url = rtrim($config['url'], '/'); + + $this->channel = !empty($config['channel']) ? $config['channel'] : null; } protected function initialize() @@ -50,6 +54,12 @@ class PearRepository extends ArrayRepository 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"); $categories = $categoryXML->getElementsByTagName("c"); @@ -80,7 +90,7 @@ class PearRepository extends ArrayRepository $packages = $packagesXML->getElementsByTagName('p'); $loader = new ArrayLoader(); foreach ($packages as $package) { - $packageName = $package->nodeValue; + $packageName = 'pear-'.$this->channel.'/'.$package->nodeValue; $packageLink = $package->getAttribute('xlink:href'); $releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink); @@ -219,7 +229,7 @@ class PearRepository extends ArrayRepository foreach ($informations as $information) { $package = $information->getElementsByTagName('p')->item(0); - $packageName = $package->getElementsByTagName('n')->item(0)->nodeValue; + $packageName = 'pear-'.$this->channel.'/'.$package->getElementsByTagName('n')->item(0)->nodeValue; $packageData = array( 'name' => $packageName, 'type' => 'library' From f3ff53b17cc87cc9c5d2ba7950de28b4231403c0 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 20 Feb 2012 12:58:27 +0100 Subject: [PATCH 64/77] Create problems directly, rather than generating impossible rules --- src/Composer/DependencyResolver/Solver.php | 36 ++++--------------- .../Test/DependencyResolver/SolverTest.php | 2 -- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index e1d6575cd..ab85c78c2 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -147,10 +147,6 @@ class Solver */ protected function createInstallOneOfRule(array $packages, $reason, $reasonData = null) { - if (empty($packages)) { - return $this->createImpossibleRule($reason, $reasonData); - } - $literals = array(); foreach ($packages as $package) { $literals[] = new Literal($package, true); @@ -200,22 +196,6 @@ class Solver 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 * @@ -972,12 +952,6 @@ class Solver foreach ($this->jobs as $job) { - if (empty($job['packages']) && $job['cmd'] == 'install') { - $this->addRule( - RuleSet::TYPE_JOB, - $this->createImpossibleRule(static::RULE_JOB_INSTALL, $job) - ); - } foreach ($job['packages'] as $package) { switch ($job['cmd']) { case 'install': @@ -1002,9 +976,13 @@ class Solver foreach ($this->jobs as $job) { switch ($job['cmd']) { case 'install': - $rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']); - $this->addRule(RuleSet::TYPE_JOB, $rule); - $this->ruleToJob[$rule->getId()] = $job; + if (empty($job['packages'])) { + $this->problems[] = array($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; case 'remove': // remove all packages with this name including uninstalled diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 70509ea0f..fe6782177 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -57,8 +57,6 @@ class SolverTest extends TestCase public function testInstallNonExistingPackageFails() { - $this->markTestIncomplete('Reporting this failure is not implemented/working yet'); - $this->repo->addPackage($this->getPackage('A', '1.0')); $this->reposComplete(); From d9b535e2309fa9dfce88fc9a1bbb68246a007ba4 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 13:01:29 +0100 Subject: [PATCH 65/77] [pear-prefix] fix installing pear packages --- src/Composer/Repository/PearRepository.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 71f8bd8a9..ed7bd8d53 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -90,7 +90,8 @@ class PearRepository extends ArrayRepository $packages = $packagesXML->getElementsByTagName('p'); $loader = new ArrayLoader(); foreach ($packages as $package) { - $packageName = 'pear-'.$this->channel.'/'.$package->nodeValue; + $packageName = $package->nodeValue; + $fullName = 'pear-'.$this->channel.'/'.$packageName; $packageLink = $package->getAttribute('xlink:href'); $releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink); @@ -112,7 +113,7 @@ class PearRepository extends ArrayRepository $pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue; $packageData = array( - 'name' => $packageName, + 'name' => $fullName, 'type' => 'library', 'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"), 'version' => $pearVersion, @@ -229,9 +230,10 @@ class PearRepository extends ArrayRepository foreach ($informations as $information) { $package = $information->getElementsByTagName('p')->item(0); - $packageName = 'pear-'.$this->channel.'/'.$package->getElementsByTagName('n')->item(0)->nodeValue; + $packageName = $package->getElementsByTagName('n')->item(0)->nodeValue; + $fullName = 'pear-'.$this->channel.'/'.$packageName; $packageData = array( - 'name' => $packageName, + 'name' => $fullName, 'type' => 'library' ); $packageKeys = array('l' => 'license', 'd' => 'description'); From be47869f56e6eda0f96bb405f6153b992fa1e844 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 14:22:16 +0100 Subject: [PATCH 66/77] [docs] add repository sections for pear and package repos --- doc/05-repositories.md | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 6c465cdff..4fdc680cb 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -148,6 +148,71 @@ 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": "trunk" + } + } + } + ], + "require": { + "smarty/smarty": "3.1.*" + } +} +``` + +Typically you would leave the source part off, as you don't really need it. + ## Hosting your own From 397ca5ddbf5cfc2c0693521f540e3003ec2bca54 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 15:32:22 +0100 Subject: [PATCH 67/77] [docs] add section to repositories about hosting your own composer repo --- doc/05-repositories.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 4fdc680cb..8b88be0ba 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -216,3 +216,28 @@ Here is an example for the smarty template engine: 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 put 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 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. From 1dc25641c04c3ae16fec26e0b1392df34d881e95 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 15:41:05 +0100 Subject: [PATCH 68/77] [docs] wrap newlines at 80 chars --- doc/05-repositories.md | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 8b88be0ba..6b6a60f84 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -217,27 +217,46 @@ 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. +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. +* **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. -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. +* **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. -There are a few different tools that can help you create a `composer` repository. +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. +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. +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 put set your own copy, simply follow the instructions from the [packagist github repository](https://github.com/composer/packagist). +To put 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. +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 from these repositories and dump a `packages.json` that is your `composer` repository. +You give it a `composer.json` containing repositories, typically VCS and package +repository definitions. It will fetch all the packages 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. +Check [the satis GitHub repository](https://github.com/composer/satis) for more +information. From 08af50220e8b1696237f06332b086e9b587975da Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 15:57:19 +0100 Subject: [PATCH 69/77] [docs] add community chapter --- doc/06-community.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 doc/06-community.md diff --git a/doc/06-community.md b/doc/06-community.md new file mode 100644 index 000000000..edb546423 --- /dev/null +++ b/doc/06-community.md @@ -0,0 +1,37 @@ +# 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. + +## 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. + +## Resources + +### Blog posts + +* [Composer: Part 1 – What & Why](http://nelm.io/blog/2011/12/composer-part-1-what-why/) +* [Composer: Part 2 – Impact](http://nelm.io/blog/2011/12/composer-part-2-impact/) +* [An Update On Composer](http://nelm.io/blog/2012/02/an-update-on-composer/) + +### Slides + +* [Dependency Management with Composer - Jordi Boggiano](http://slides.seld.be/?file=2012-02-14+Dependency+Management+with+Composer.html) +* [Composer - Nils Adermann](http://www.slideshare.net/naderman/composer-php-usergroup-karlsruhe) From 1f6ac63d8559c795ab1f7d379284bee0f0c21323 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 16:05:18 +0100 Subject: [PATCH 70/77] [docs] remove resources from community --- doc/06-community.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/doc/06-community.md b/doc/06-community.md index edb546423..72a257589 100644 --- a/doc/06-community.md +++ b/doc/06-community.md @@ -22,16 +22,3 @@ 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. - -## Resources - -### Blog posts - -* [Composer: Part 1 – What & Why](http://nelm.io/blog/2011/12/composer-part-1-what-why/) -* [Composer: Part 2 – Impact](http://nelm.io/blog/2011/12/composer-part-2-impact/) -* [An Update On Composer](http://nelm.io/blog/2012/02/an-update-on-composer/) - -### Slides - -* [Dependency Management with Composer - Jordi Boggiano](http://slides.seld.be/?file=2012-02-14+Dependency+Management+with+Composer.html) -* [Composer - Nils Adermann](http://www.slideshare.net/naderman/composer-php-usergroup-karlsruhe) From b71db7607d95705f2c099f767e22914507eb9b1d Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 16:06:28 +0100 Subject: [PATCH 71/77] [docs] fix smarty source reference --- doc/04-schema.md | 2 +- doc/05-repositories.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 2c9383ee3..9660584ba 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -288,7 +288,7 @@ Example: "source": { "url": "http://smarty-php.googlecode.com/svn/", "type": "svn", - "reference": "trunk" + "reference": "tags/Smarty_3_1_7/distribution/" } } } diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 6b6a60f84..cd577dbf7 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -202,7 +202,7 @@ Here is an example for the smarty template engine: "source": { "url": "http://smarty-php.googlecode.com/svn/", "type": "svn", - "reference": "trunk" + "reference": "tags/Smarty_3_1_7/distribution/" } } } From 9cc02a7596bf82bb37f611170e4cac9dee6cdaff Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 16:10:38 +0100 Subject: [PATCH 72/77] [docs] minor fixes --- doc/05-repositories.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index cd577dbf7..4cdcf866a 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -246,7 +246,7 @@ 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 put set your own copy, simply follow the instructions from the [packagist +To set your own copy, simply follow the instructions from the [packagist github repository](https://github.com/composer/packagist). ### Satis @@ -255,8 +255,9 @@ 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 from these repositories -and dump a `packages.json` that is your `composer` repository. +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. From e4143f98c5a40fab19ebddf3cb5789b1b35d7806 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Mon, 20 Feb 2012 16:25:11 +0100 Subject: [PATCH 73/77] [docs] mention coding standards --- README.md | 4 ++++ doc/06-community.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 9dae242e9..d0ca984a1 100644 --- a/README.md +++ b/README.md @@ -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. +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). Community diff --git a/doc/06-community.md b/doc/06-community.md index 72a257589..35a92092a 100644 --- a/doc/06-community.md +++ b/doc/06-community.md @@ -15,6 +15,10 @@ The most important guidelines are described as follows: > 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 From 38ef1dcb24e85e7b01a50817c9c0634e541bf0a0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 16:18:31 +0100 Subject: [PATCH 74/77] Doc fixes --- doc/00-intro.md | 8 ++++---- doc/02-libraries.md | 14 +++++++------- doc/04-schema.md | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index ec4ccc6b4..4d35b6924 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -14,10 +14,10 @@ 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 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. +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 diff --git a/doc/02-libraries.md b/doc/02-libraries.md index adfe88c91..530c17c02 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -154,15 +154,15 @@ 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 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. +[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, at which point packagist will start crawling it. Once it -is done, your package will be available to anyone. +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. diff --git a/doc/04-schema.md b/doc/04-schema.md index 9660584ba..24da0ddfe 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -412,6 +412,6 @@ Optional. A set of files that should be treated as binaries and symlinked into the `bin- dir` (from config). -See [faq/bin.md] for more details. +See [articles/bin.md] for more details. Optional. From 46931824fb383b47ad100f9f7ff8479dcd7fce9f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 16:18:58 +0100 Subject: [PATCH 75/77] Rename faqs to articles --- doc/{faqs => articles}/packagist-update-schedule.md | 0 doc/{faqs => articles}/scripts.md | 0 doc/{faqs => articles}/vendor-bins.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename doc/{faqs => articles}/packagist-update-schedule.md (100%) rename doc/{faqs => articles}/scripts.md (100%) rename doc/{faqs => articles}/vendor-bins.md (100%) diff --git a/doc/faqs/packagist-update-schedule.md b/doc/articles/packagist-update-schedule.md similarity index 100% rename from doc/faqs/packagist-update-schedule.md rename to doc/articles/packagist-update-schedule.md diff --git a/doc/faqs/scripts.md b/doc/articles/scripts.md similarity index 100% rename from doc/faqs/scripts.md rename to doc/articles/scripts.md diff --git a/doc/faqs/vendor-bins.md b/doc/articles/vendor-bins.md similarity index 100% rename from doc/faqs/vendor-bins.md rename to doc/articles/vendor-bins.md From a71f9c71f01f2511ae09d216f6804f838bd0e5fa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 16:35:13 +0100 Subject: [PATCH 76/77] Move schema into res folder --- doc/04-schema.md | 2 +- {Resources => res}/composer-schema.json | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {Resources => res}/composer-schema.json (100%) diff --git a/doc/04-schema.md b/doc/04-schema.md index 24da0ddfe..fa947094c 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -7,7 +7,7 @@ This chapter will explain all of the options available in `composer.json`. 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/docs/Resources +schema.json`](https://github.com/composer/composer/blob/res /composer-schema.json). ## Package root diff --git a/Resources/composer-schema.json b/res/composer-schema.json similarity index 100% rename from Resources/composer-schema.json rename to res/composer-schema.json From b8f200b0f2159e066ade63a3066c790795274e55 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Feb 2012 16:53:49 +0100 Subject: [PATCH 77/77] Fix link --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index fa947094c..2e1c3d2f6 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -7,7 +7,7 @@ This chapter will explain all of the options available in `composer.json`. 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/res +schema.json`](https://github.com/composer/composer/blob/master/res /composer-schema.json). ## Package root