Merge remote-tracking branch 'parent/master'

Conflicts:
	src/Composer/Factory.php
main
Nicolas Toniazzi 10 years ago
commit 865eab602f

@ -0,0 +1,29 @@
Contributing to Composer
========================
Installation from Source
------------------------
Prior to contributing to Composer, you must use be able to run the tests.
To achieve this, you must use the sources and not the phar file.
1. Run `git clone https://github.com/composer/composer.git`
2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable
3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install`
You can now run Composer by executing the `bin/composer` script: `php /path/to/composer/bin/composer`
Contributing policy
-------------------
All code contributions - including those of people having commit access -
must go through a pull request and approved by a core developer before being
merged. This is to ensure proper review of all the code.
Fork the project, create a feature branch, and send us a pull request.
To ensure a consistent code base, you should make sure the code follows
the [Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html)
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).

@ -32,18 +32,6 @@ themselves. To create libraries/packages please read the
3. Run Composer: `php composer.phar install`
4. Browse for more packages on [Packagist](https://packagist.org).
Installation from Source
------------------------
To run tests, or develop Composer itself, you must use the sources and not the phar
file as described above.
1. Run `git clone https://github.com/composer/composer.git`
2. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable
3. Run Composer to get the dependencies: `cd composer && php ../composer.phar install`
You can now run Composer by executing the `bin/composer` script: `php /path/to/composer/bin/composer`
Global installation of Composer (manual)
----------------------------------------
@ -55,20 +43,6 @@ Updating Composer
Running `php composer.phar self-update` or equivalent will update a phar
install with the latest version.
Contributing
------------
All code contributions - including those of people having commit access -
must go through a pull request and approved by a core developer before being
merged. This is to ensure proper review of all the code.
Fork the project, create a feature branch, and send us a pull request.
To ensure a consistent code base, you should make sure the code follows
the [Coding Standards](http://symfony.com/doc/current/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
---------

@ -23,7 +23,7 @@
},
"require": {
"php": ">=5.3.2",
"justinrainbow/json-schema": "~1.1",
"justinrainbow/json-schema": "~1.3",
"seld/jsonlint": "~1.0",
"symfony/console": "~2.3",
"symfony/finder": "~2.2",

33
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "0430383b5ba00e406ce4a44253f49fe7",
"hash": "2bc9cc8aa706b68d611d7058e4eb8de7",
"packages": [
{
"name": "justinrainbow/json-schema",
@ -327,16 +327,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "2.0.13",
"version": "2.0.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5"
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5",
"reference": "0e7d2eec5554f869fa7a4ec2d21e4b37af943ea5",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca158276c1200cc27f5409a5e338486bc0b4fc94",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94",
"shasum": ""
},
"require": {
@ -388,7 +388,7 @@
"testing",
"xunit"
],
"time": "2014-12-03 06:41:44"
"time": "2014-12-26 13:28:33"
},
{
"name": "phpunit/php-file-iterator",
@ -574,16 +574,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.4.0",
"version": "4.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0"
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0",
"reference": "bbe7bcb83b6ec1a9eaabbe1b70d4795027c53ee0",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a5e49a86ce5e33b8d0657abe145057fc513543a",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a",
"shasum": ""
},
"require": {
@ -641,7 +641,7 @@
"testing",
"xunit"
],
"time": "2014-12-05 06:49:03"
"time": "2014-12-28 07:57:05"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -982,16 +982,16 @@
},
{
"name": "sebastian/version",
"version": "1.0.3",
"version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43"
"reference": "a77d9123f8e809db3fbdea15038c27a95da4058b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
"reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/a77d9123f8e809db3fbdea15038c27a95da4058b",
"reference": "a77d9123f8e809db3fbdea15038c27a95da4058b",
"shasum": ""
},
"type": "library",
@ -1013,7 +1013,7 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2014-03-07 15:35:33"
"time": "2014-12-15 14:25:24"
},
{
"name": "symfony/yaml",
@ -1067,6 +1067,7 @@
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.3.2"
},

@ -107,6 +107,8 @@ mv composer.phar /usr/local/bin/composer
> **Note:** If the above fails due to permissions, run the `mv` line
> again with sudo.
> **Note:** In OSX Yosemite the `/usr` directory does not exist by default. If you receive the error "/usr/local/bin/composer: No such file or directory" then you must create `/usr/local/bin/` manually before proceeding.
Then, just run `composer` in order to run Composer instead of `php composer.phar`.
## Installation - Windows

@ -2,7 +2,7 @@
## Installing
If you have not yet installed Composer, refer to to the [Intro](00-intro.md) chapter.
If you have not yet installed Composer, refer to the [Intro](00-intro.md) chapter.
## `composer.json`: Project Setup
@ -213,7 +213,7 @@ You define a mapping from namespaces to directories. The `src` directory would
be in your project root, on the same level as `vendor` directory is. An example
filename would be `src/Foo.php` containing an `Acme\Foo` class.
After adding the `autoload` field, you have to re-run `install` to re-generate
After adding the `autoload` field, you have to re-run `dump-autoload` to re-generate
the `vendor/autoload.php` file.
Including that file will also return the autoloader instance, so you can store

@ -77,8 +77,8 @@ you can just add a `version` field:
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
of `-patch`, `-alpha`, `-beta` or `-RC`. The suffixes can also be followed by
a number.
of `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`. The suffixes
can also be followed by a number.
Here are a few examples of valid tag names:

@ -87,7 +87,8 @@ resolution.
installing a package, you can use `--dry-run`. This will simulate the
installation and show you what would happen.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`.
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
* **--no-autoloader:** Skips autoloader generation.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-plugins:** Disables plugins.
* **--no-progress:** Removes the progress display that can mess with some
@ -129,7 +130,8 @@ php composer.phar update vendor/*
fulfill these.
* **--dry-run:** Simulate the command without actually doing anything.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`.
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
* **--no-autoloader:** Skips autoloader generation.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-plugins:** Disables plugins.
* **--no-progress:** Removes the progress display that can mess with some
@ -397,16 +399,18 @@ options.
### Options
* **--global (-g):** Operate on the global config file located at
`$COMPOSER_HOME/config.json` by default. Without this option, this command
affects the local composer.json file or a file specified by `--file`.
`$COMPOSER_HOME/config.json` by default. Without this option, this command
affects the local composer.json file or a file specified by `--file`.
* **--editor (-e):** Open the local composer.json file using in a text editor as
defined by the `EDITOR` env variable. With the `--global` option, this opens
the global config file.
defined by the `EDITOR` env variable. With the `--global` option, this opens
the global config file.
* **--unset:** Remove the configuration element named by `setting-key`.
* **--list (-l):** Show the list of current config variables. With the `--global`
option this lists the global configuration only.
option this lists the global configuration only.
* **--file="..." (-f):** Operate on a specific file instead of composer.json. Note
that this cannot be used in conjunction with the `--global` option.
that this cannot be used in conjunction with the `--global` option.
* **--absolute:** Returns absolute paths when fetching *-dir config values
instead of relative.
### Modifying Repositories
@ -463,6 +467,9 @@ By default the command checks for the packages on packagist.org.
* **--keep-vcs:** Skip the deletion of the VCS metadata for the created
project. This is mostly useful if you run the command in non-interactive
mode.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
## dump-autoload

@ -54,8 +54,8 @@ The version of the package. In most cases this is not required and should
be omitted (see below).
This must follow the format of `X.Y.Z` or `vX.Y.Z` with an optional suffix
of `-dev`, `-patch`, `-alpha`, `-beta` or `-RC`. The patch, alpha, beta and
RC suffixes can also be followed by a number.
of `-dev`, `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`.
The patch, alpha, beta and RC suffixes can also be followed by a number.
Examples:
@ -67,6 +67,7 @@ Examples:
- 1.0.0-alpha3
- 1.0.0-beta2
- 1.0.0-RC5
- v2.0.4-p1
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
@ -375,7 +376,7 @@ useful for common interfaces. A package could depend on some virtual
`logger` package, any library that implements this logger interface would
simply list it in `provide`.
### suggest
#### suggest
Suggested packages that can enhance or work well with this package. These are
just informational and are displayed after the package is installed, to give
@ -790,6 +791,9 @@ The following options are supported:
the generated Composer autoloader. When null a random one will be generated.
* **optimize-autoloader** Defaults to `false`. Always optimize when dumping
the autoloader.
* **classmap-authoritative:** Defaults to `false`. If true, the composer
autoloader will not scan the filesystem for classes that are not found in
the class map. Implies 'optimize-autoloader'.
* **github-domains:** Defaults to `["github.com"]`. A list of domains to use in
github mode. This is used for GitHub Enterprise setups.
* **github-expose-hostname:** Defaults to `true`. If set to false, the OAuth

@ -122,7 +122,7 @@ JSON request body:
```json
{
"downloads": [
{"name": "monolog/monolog", "version": "1.2.1.0"},
{"name": "monolog/monolog", "version": "1.2.1.0"}
]
}
```

@ -38,10 +38,14 @@ specifying a `branch-alias` field under `extra` in `composer.json`:
}
```
The branch version must begin with `dev-` (non-comparable version), the alias
must be a comparable dev version (i.e. start with numbers, and end with
`.x-dev`). The `branch-alias` must be present on the branch that it references.
For `dev-master`, you need to commit it on the `master` branch.
If you alias a non-comparible version (such as dev-develop) `dev-` must prefix the
branch name. You may also alias a comparible version (i.e. start with numbers,
and end with `.x-dev`), but only as a more specific version.
For example, 1.x-dev could be aliased as 1.2.x-dev.
The alias must be a comparable dev version, and the `branch-alias` must be present on
the branch that it references. For `dev-master`, you need to commit it on the
`master` branch.
As a result, anyone can now require `1.0.*` and it will happily install
`dev-master`.

@ -66,7 +66,7 @@ constraint if you want really specific versions.
}
```
Once you did this, you just run `php bin/satis build <configuration file> <build dir>`.
Once you've done this, you just run `php bin/satis build <configuration file> <build dir>`.
For example `php bin/satis build config.json web/` would read the `config.json`
file and build a static repository inside the `web/` directory.

@ -40,7 +40,7 @@ username/password pairs, for example:
```json
{
"basic-auth": [
"basic-auth": {
"repo.example1.org": {
"username": "my-username1",
"password": "my-secret-password1"
@ -49,7 +49,7 @@ username/password pairs, for example:
"username": "my-username2",
"password": "my-secret-password2"
}
]
}
}
```

@ -114,8 +114,9 @@ php -d memory_limit=-1 composer.phar <...>
## "The system cannot find the path specified" (Windows)
1. Open regedit.
2. Search for an ```AutoRun``` key inside ```HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor```
or ```HKEY_CURRENT_USER\Software\Microsoft\Command Processor```.
2. Search for an `AutoRun` key inside `HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor`,
`HKEY_CURRENT_USER\Software\Microsoft\Command Processor`
or `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Command Processor`.
3. Check if it contains any path to non-existent file, if it's the case, just remove them.
## API rate limit and OAuth tokens

@ -1,12 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"name": "Package",
"type": "object",
"additionalProperties": false,
"required": [ "name", "description" ],
"properties": {
"name": {
"type": "string",
"description": "Package name, including 'vendor-name/' prefix.",
"required": true
"description": "Package name, including 'vendor-name/' prefix."
},
"type": {
"description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.",
@ -18,8 +19,7 @@
},
"description": {
"type": "string",
"description": "Short package description.",
"required": true
"description": "Short package description."
},
"keywords": {
"type": "array",
@ -51,11 +51,11 @@
"items": {
"type": "object",
"additionalProperties": false,
"required": [ "name"],
"properties": {
"name": {
"type": "string",
"description": "Full name of the author.",
"required": true
"description": "Full name of the author."
},
"email": {
"type": "string",
@ -197,6 +197,10 @@
"type": "boolean",
"description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true."
},
"classmap-authoritative": {
"type": "boolean",
"description": "If true, the composer autoloader will not scan the filesystem for classes that are not found in the class map, defaults to false."
},
"github-domains": {
"type": "array",
"description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].",

@ -63,6 +63,7 @@ class AutoloadGenerator
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
$useGlobalIncludePath = (bool) $config->get('use-include-path');
$prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
$classMapAuthoritative = $config->get('classmap-authoritative');
$targetDir = $vendorPath.'/'.$targetDir;
$filesystem->ensureDirectoryExists($targetDir);
@ -226,7 +227,7 @@ EOF;
file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
}
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader));
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative));
// use stream_copy_to_stream instead of copy
// to work around https://bugs.php.net/bug.php?id=64634
@ -443,7 +444,7 @@ return ComposerAutoloaderInit$suffix::getLoader();
AUTOLOAD;
}
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $classMapAuthoritative)
{
// TODO the class ComposerAutoloaderInit should be revert to a closure
// when APC has been fixed:
@ -520,6 +521,13 @@ PSR4;
CLASSMAP;
}
if ($classMapAuthoritative) {
$file .= <<<'CLASSMAPAUTHORITATIVE'
$loader->setClassMapAuthoritative(true);
CLASSMAPAUTHORITATIVE;
}
if ($useGlobalIncludePath) {
$file .= <<<'INCLUDEPATH'
$loader->setUseIncludePath(true);

@ -54,6 +54,8 @@ class ClassLoader
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -248,6 +250,27 @@ class ClassLoader
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
@ -299,6 +322,9 @@ class ClassLoader
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');

@ -16,6 +16,8 @@ use Composer\Composer;
use Composer\Console\Application;
use Composer\IO\IOInterface;
use Composer\IO\NullIO;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command as BaseCommand;
/**
@ -102,4 +104,16 @@ abstract class Command extends BaseCommand
{
$this->io = $io;
}
/**
* {@inheritDoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(array('--no-ansi')) && $input->hasOption('no-progress')) {
$input->setOption('no-progress', true);
}
parent::initialize($input, $output);
}
}

@ -57,6 +57,7 @@ class ConfigCommand extends Command
new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json', 'composer.json'),
new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
new InputArgument('setting-key', null, 'Setting key'),
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
))
@ -99,6 +100,8 @@ EOT
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
if ($input->getOption('global') && 'composer.json' !== $input->getOption('file')) {
throw new \RuntimeException('--file and --global can not be combined');
}
@ -134,7 +137,7 @@ EOT
}
if (!$this->configFile->exists()) {
throw new \RuntimeException('No composer.json found in the current directory');
throw new \RuntimeException(sprintf('File "%s" cannot be found in the current directory', $configFile));
}
}
@ -218,7 +221,7 @@ EOT
$value = $data;
} elseif (isset($data['config'][$settingKey])) {
$value = $data['config'][$settingKey];
$value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS);
} else {
throw new \RuntimeException($settingKey.' is not defined');
}
@ -322,6 +325,7 @@ EOT
),
'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
'classmap-authoritative' => array($booleanValidator, $booleanNormalizer),
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
'github-expose-hostname' => array($booleanValidator, $booleanNormalizer),
);

@ -69,6 +69,7 @@ class CreateProjectCommand extends Command
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deletion vcs folder.'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
))
->setHelp(<<<EOT
The <info>create-project</info> command creates a new project from a given
@ -125,11 +126,12 @@ EOT
$input->getOption('keep-vcs'),
$input->getOption('no-progress'),
$input->getOption('no-install'),
$input->getOption('ignore-platform-reqs'),
$input
);
}
public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, InputInterface $input)
public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, InputInterface $input)
{
$oldCwd = getcwd();
@ -159,7 +161,8 @@ EOT
$installer->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode($installDevPackages)
->setRunScripts( ! $noScripts);
->setRunScripts(!$noScripts)
->setIgnorePlatformRequirements($ignorePlatformReqs);
if ($disablePlugins) {
$installer->disablePlugins();

@ -52,7 +52,7 @@ EOT
$package = $composer->getPackage();
$config = $composer->getConfig();
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
if ($optimize) {
$output->writeln('<info>Generating optimized autoload files</info>');

@ -40,12 +40,14 @@ class HomeCommand extends Command
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Package(s) to browse to.'),
new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'),
new InputOption('show', 's', InputOption::VALUE_NONE, 'Only show the homepage or repository URL.'),
))
->setHelp(<<<EOT
The home command opens a package's repository URL or
The home command opens or shows a package's repository URL or
homepage in your default browser.
To open the homepage by default, use -H or --homepage.
To show instead of open the repository or homepage URL, use -s or --show.
EOT
);
}
@ -55,7 +57,7 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$repo = $this->initializeRepo($input, $output);
$repo = $this->initializeRepo();
$return = 0;
foreach ($input->getArgument('packages') as $packageName) {
@ -81,7 +83,11 @@ EOT
continue;
}
$this->openBrowser($url);
if ($input->getOption('show')) {
$output->writeln(sprintf('<info>%s</info>', $url));
} else {
$this->openBrowser($url);
}
}
return $return;
@ -138,13 +144,11 @@ EOT
}
/**
* initializes the repo
* Initializes the repo
*
* @param InputInterface $input
* @param OutputInterface $output
* @return CompositeRepository
*/
private function initializeRepo(InputInterface $input, OutputInterface $output)
private function initializeRepo()
{
$composer = $this->getComposer(false);

@ -41,6 +41,7 @@ class InstallCommand extends Command
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
@ -74,6 +75,10 @@ EOT
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$output->writeln('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$io = $this->getIO();
@ -105,7 +110,7 @@ EOT
$preferDist = $input->getOption('prefer-dist');
}
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
$install
->setDryRun($input->getOption('dry-run'))
@ -113,6 +118,7 @@ EOT
->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setOptimizeAutoloader($optimize)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))

@ -38,7 +38,7 @@ class RequireCommand extends InitCommand
->setName('require')
->setDescription('Adds required packages to your composer.json and installs them')
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Required package with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Required package name optionally including a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'),
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
@ -50,7 +50,9 @@ class RequireCommand extends InitCommand
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
))
->setHelp(<<<EOT
The require command adds required packages to your composer.json and installs them
The require command adds required packages to your composer.json and installs them.
If you do not specify a version constraint, composer will choose a suitable one based on the available package versions.
If you do not want to install the new dependencies immediately you can call it with --no-update

@ -173,7 +173,7 @@ EOT
protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
{
try {
@chmod($newFilename, 0777 & ~umask());
@chmod($newFilename, fileperms($localFilename));
if (!ini_get('phar.readonly')) {
// test the phar validity
$phar = new \Phar($newFilename);

@ -41,6 +41,7 @@ class ShowCommand extends Command
{
$this
->setName('show')
->setAliases(array('info'))
->setDescription('Show information about packages')
->setDefinition(array(
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect'),

@ -41,6 +41,7 @@ class UpdateCommand extends Command
new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'),
new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
@ -78,6 +79,10 @@ EOT
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$output->writeln('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$io = $this->getIO();
@ -109,7 +114,7 @@ EOT
$preferDist = $input->getOption('prefer-dist');
}
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader') || $config->get('classmap-authoritative');
$install
->setDryRun($input->getOption('dry-run'))
@ -117,6 +122,7 @@ EOT
->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setOptimizeAutoloader($optimize)
->setUpdate(true)

@ -19,6 +19,8 @@ use Composer\Config\ConfigSourceInterface;
*/
class Config
{
const RELATIVE_PATHS = 1;
public static $defaultConfig = array(
'process-timeout' => 300,
'use-include-path' => false,
@ -38,6 +40,7 @@ class Config
'discard-changes' => false,
'autoloader-suffix' => null,
'optimize-autoloader' => false,
'classmap-authoritative' => false,
'prepend-autoloader' => true,
'github-domains' => array('github.com'),
'github-expose-hostname' => true,
@ -56,6 +59,7 @@ class Config
);
private $config;
private $baseDir;
private $repositories;
private $configSource;
private $authConfigSource;
@ -64,12 +68,13 @@ class Config
/**
* @param boolean $useEnvironment Use COMPOSER_ environment variables to replace config settings
*/
public function __construct($useEnvironment = true)
public function __construct($useEnvironment = true, $baseDir = null)
{
// load defaults
$this->config = static::$defaultConfig;
$this->repositories = static::$defaultRepositories;
$this->useEnvironment = (bool) $useEnvironment;
$this->baseDir = $baseDir;
}
public function setConfigSource(ConfigSourceInterface $source)
@ -121,7 +126,7 @@ class Config
}
// disable a repository with an anonymous {"name": false} repo
if (1 === count($repository) && false === current($repository)) {
if (is_array($repository) && 1 === count($repository) && false === current($repository)) {
unset($this->repositories[key($repository)]);
continue;
}
@ -149,10 +154,11 @@ class Config
* Returns a setting
*
* @param string $key
* @param int $flags Options (see class constants)
* @throws \RuntimeException
* @return mixed
*/
public function get($key)
public function get($key, $flags = 0)
{
switch ($key) {
case 'vendor-dir':
@ -166,10 +172,14 @@ class Config
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
$val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key]), '/\\');
$val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key], $flags), '/\\');
$val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val);
return $val;
if (substr($key, -4) !== '-dir') {
return $val;
}
return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val);
case 'cache-ttl':
return (int) $this->config[$key];
@ -205,7 +215,7 @@ class Config
return (int) $this->config['cache-ttl'];
case 'home':
return rtrim($this->process($this->config[$key]), '/\\');
return rtrim($this->process($this->config[$key], $flags), '/\\');
case 'discard-changes':
if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) {
@ -242,17 +252,17 @@ class Config
return null;
}
return $this->process($this->config[$key]);
return $this->process($this->config[$key], $flags);
}
}
public function all()
public function all($flags = 0)
{
$all = array(
'repositories' => $this->getRepositories(),
);
foreach (array_keys($this->config) as $key) {
$all['config'][$key] = $this->get($key);
$all['config'][$key] = $this->get($key, $flags);
}
return $all;
@ -281,9 +291,10 @@ class Config
* Replaces {$refs} inside a config string
*
* @param string $value a config string that can contain {$refs-to-other-config}
* @param int $flags Options (see class constants)
* @return string
*/
private function process($value)
private function process($value, $flags)
{
$config = $this;
@ -291,11 +302,28 @@ class Config
return $value;
}
return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config) {
return $config->get($match[1]);
return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config, $flags) {
return $config->get($match[1], $flags);
}, $value);
}
/**
* Turns relative paths in absolute paths without realpath()
*
* Since the dirs might not exist yet we can not call realpath or it will fail.
*
* @param string $path
* @return string
*/
private function realpath($path)
{
if (substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':') {
return $path;
}
return $this->baseDir . '/' . $path;
}
/**
* Reads the value of a Composer environment variable
*

@ -184,6 +184,7 @@ class Application extends BaseApplication
$minSpaceFree = 1024*1024;
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
|| (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
) {
$output->writeln('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
}

@ -90,10 +90,10 @@ class FileDownloader implements DownloaderInterface
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('');
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
$this->io->write('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write('');
$this->io->write(' Failed, trying the next URL');
$this->io->write(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')');
}
if (!count($urls)) {

@ -44,6 +44,7 @@ class EventDispatcher
protected $io;
protected $loader;
protected $process;
protected $listeners;
/**
* Constructor.

@ -17,10 +17,9 @@ use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\Package\Archiver;
use Composer\Repository\RepositoryManager;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\WritableRepositoryInterface;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Autoload\AutoloadGenerator;
@ -36,7 +35,6 @@ use Composer\Package\Version\VersionParser;
*/
class Factory
{
/**
* @return string
* @throws \RuntimeException
@ -73,9 +71,9 @@ class Factory
if (!$xdgConfig) {
$xdgConfig = $userDir . '/.config';
}
$home = $xdgConfig . '/composer';
} else {
$home = $userDir . '/.composer';
$home = $xdgConfig . '/composer';
} else {
$home = $userDir . '/.composer';
}
}
}
@ -144,8 +142,10 @@ class Factory
* @param IOInterface|null $io
* @return Config
*/
public static function createConfig(IOInterface $io = null)
public static function createConfig(IOInterface $io = null, $cwd = null)
{
$cwd = $cwd ?: getcwd();
// determine home and cache dirs
$home = self::getHomeDir();
$cacheDir = self::getCacheDir($home);
@ -188,7 +188,7 @@ class Factory
}
}
$config = new Config();
$config = new Config(true, $cwd);
// add dirs to the config
$config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir, 'data-dir' => $dataDir)));
@ -218,14 +218,14 @@ class Factory
public static function getComposerFile()
{
return trim(getenv('COMPOSER')) ? : './composer.json';
return trim(getenv('COMPOSER')) ?: './composer.json';
}
public static function createAdditionalStyles()
{
return array(
'highlight' => new OutputFormatterStyle('red'),
'warning' => new OutputFormatterStyle('black', 'yellow'),
'warning' => new OutputFormatterStyle('black', 'yellow'),
);
}
@ -241,15 +241,18 @@ class Factory
throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager');
}
$factory = new static;
$rm = $factory->createRepositoryManager($io, $config);
$rm = $factory->createRepositoryManager($io, $config);
}
foreach ($config->getRepositories() as $index => $repo) {
if (is_string($repo)) {
throw new \UnexpectedValueException('"repositories" should be an array of repository definitions, only a single repository was given');
}
if (!is_array($repo)) {
throw new \UnexpectedValueException('Repository ' . $index . ' (' . json_encode($repo) . ') should be an array, ' . gettype($repo) . ' given');
throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given');
}
if (!isset($repo['type'])) {
throw new \UnexpectedValueException('Repository ' . $index . ' (' . json_encode($repo) . ') must have a type defined');
throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') must have a type defined');
}
$name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index;
while (isset($repos[$name])) {
@ -268,12 +271,15 @@ class Factory
* @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will
* read from the default filename
* @param bool $disablePlugins Whether plugins should not be loaded
* @param bool $fullLoad Whether to initialize everything or only main project stuff (used when loading the global composer)
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
* @return Composer
*/
public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false)
public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true)
{
$cwd = $cwd ?: getcwd();
// load Composer configuration
if (null === $localConfig) {
$localConfig = static::getComposerFile();
@ -281,16 +287,16 @@ class Factory
if (is_string($localConfig)) {
$composerFile = $localConfig;
$file = new JsonFile($localConfig, new RemoteFilesystem($io));
$file = new JsonFile($localConfig, new RemoteFilesystem($io));
if (!$file->exists()) {
if ($localConfig === './composer.json' || $localConfig === 'composer.json') {
$message = 'Composer could not find a composer.json file in ' . getcwd();
$message = 'Composer could not find a composer.json file in '.$cwd;
} else {
$message = 'Composer could not find the config file: ' . $localConfig;
$message = 'Composer could not find the config file: '.$localConfig;
}
$instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
throw new \InvalidArgumentException($message . PHP_EOL . $instructions);
throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
}
$file->validateSchema(JsonFile::LAX_SCHEMA);
@ -298,7 +304,7 @@ class Factory
}
// Load config and override with local config/auth config
$config = static::createConfig($io);
$config = static::createConfig($io, $cwd);
$config->merge($localConfig);
if (isset($composerFile)) {
if ($io && $io->isDebug()) {
@ -314,69 +320,77 @@ class Factory
}
}
// load auth configs into the IO instance
$io->loadConfiguration($config);
$vendorDir = $config->get('vendor-dir');
$binDir = $config->get('bin-dir');
// setup process timeout
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
$binDir = $config->get('bin-dir');
// initialize composer
$composer = new Composer();
$composer->setConfig($config);
if ($fullLoad) {
// load auth configs into the IO instance
$io->loadConfiguration($config);
// setup process timeout
ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
}
// initialize event dispatcher
$dispatcher = new EventDispatcher($composer, $io);
$composer->setEventDispatcher($dispatcher);
// initialize repository manager
$rm = $this->createRepositoryManager($io, $config, $dispatcher);
$composer->setRepositoryManager($rm);
// load local repository
$this->addLocalRepository($rm, $vendorDir);
// load package
$parser = new VersionParser;
$parser = new VersionParser;
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io));
$package = $loader->load($localConfig);
$composer->setPackage($package);
// initialize installation manager
$im = $this->createInstallationManager();
// Composer composition
$composer->setPackage($package);
$composer->setRepositoryManager($rm);
$composer->setInstallationManager($im);
// initialize download manager
$dm = $this->createDownloadManager($io, $config, $dispatcher);
$composer->setDownloadManager($dm);
$composer->setEventDispatcher($dispatcher);
if ($fullLoad) {
// initialize download manager
$dm = $this->createDownloadManager($io, $config, $dispatcher);
$composer->setDownloadManager($dm);
// initialize autoload generator
$generator = new AutoloadGenerator($dispatcher, $io);
$composer->setAutoloadGenerator($generator);
// initialize autoload generator
$generator = new AutoloadGenerator($dispatcher, $io);
$composer->setAutoloadGenerator($generator);
}
// add installers to the manager
// add installers to the manager (must happen after download manager is created since they read it out of $composer)
$this->createDefaultInstallers($im, $composer, $io);
$globalRepository = $this->createGlobalRepository($config, $vendorDir);
$pm = $this->createPluginManager($composer, $io, $globalRepository);
$composer->setPluginManager($pm);
if ($fullLoad) {
$globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins);
$pm = $this->createPluginManager($io, $composer, $globalComposer);
$composer->setPluginManager($pm);
if (!$disablePlugins) {
$pm->loadInstalledPlugins();
}
if (!$disablePlugins) {
$pm->loadInstalledPlugins();
}
// purge packages if they have been deleted on the filesystem
$this->purgePackages($rm, $im);
// once we have plugins and custom installers we can
// purge packages from local repos if they have been deleted on the filesystem
if ($rm->getLocalRepository()) {
$this->purgePackages($rm->getLocalRepository(), $im);
}
}
// init locker if possible
if (isset($composerFile)) {
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock';
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
if ($fullLoad && isset($composerFile)) {
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
? substr($composerFile, 0, -4).'lock'
: $composerFile . '.lock';
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
$composer->setLocker($locker);
}
@ -411,26 +425,29 @@ class Factory
*/
protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
{
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir . '/composer/installed.json')));
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json')));
}
/**
* @param Config $config
* @param string $vendorDir
* @return Repository\InstalledFilesystemRepository|null
* @param Config $config
* @return Composer|null
*/
protected function createGlobalRepository(Config $config, $vendorDir)
protected function createGlobalComposer(IOInterface $io, Config $config, $disablePlugins)
{
if ($config->get('home') == $vendorDir) {
return null;
if (realpath($config->get('home')) === getcwd()) {
return;
}
$path = $config->get('home') . '/vendor/composer/installed.json';
if (!file_exists($path)) {
return null;
$composer = null;
try {
$composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
} catch (\Exception $e) {
if ($io->isDebug()) {
$io->write('Failed to initialize global composer: '.$e->getMessage());
}
}
return new Repository\InstalledFilesystemRepository(new JsonFile($path));
return $composer;
}
/**
@ -495,14 +512,14 @@ class Factory
}
/**
* @param Composer $composer
* @param IOInterface $io
* @param RepositoryInterface $globalRepository
* @param Composer $composer
* @param Composer $globalComposer
* @return Plugin\PluginManager
*/
protected function createPluginManager(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null)
protected function createPluginManager(IOInterface $io, Composer $composer, Composer $globalComposer = null)
{
return new Plugin\PluginManager($io, $composer, $globalRepository);
return new Plugin\PluginManager($io, $composer, $globalComposer);
}
/**
@ -527,12 +544,11 @@ class Factory
}
/**
* @param Repository\RepositoryManager $rm
* @param Installer\InstallationManager $im
* @param WritableRepositoryInterface $repo repository to purge packages from
* @param Installer\InstallationManager $im manager to check whether packages are still installed
*/
protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im)
protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im)
{
$repo = $rm->getLocalRepository();
foreach ($repo->getPackages() as $package) {
if (!$im->isPackageInstalled($repo, $package)) {
$repo->removePackage($package);
@ -553,5 +569,4 @@ class Factory
return $factory->createComposer($io, $config, $disablePlugins);
}
}

@ -96,13 +96,11 @@ class ConsoleIO extends BaseIO
public function write($messages, $newline = true)
{
if (null !== $this->startTime) {
$messages = (array) $messages;
$messages[0] = sprintf(
'[%.1fMB/%.2fs] %s',
memory_get_usage() / 1024 / 1024,
microtime(true) - $this->startTime,
$messages[0]
);
$memoryUsage = memory_get_usage() / 1024 / 1024;
$timeSpent = microtime(true) - $this->startTime;
$messages = array_map(function ($message) use ($memoryUsage, $timeSpent) {
return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message);
}, (array) $messages);
}
$this->output->write($messages, $newline);
$this->lastMessage = join($newline ? "\n" : '', (array) $messages);
@ -113,6 +111,14 @@ class ConsoleIO extends BaseIO
*/
public function overwrite($messages, $newline = true, $size = null)
{
if (!$this->output->isDecorated()) {
if (!$messages) {
return;
}
return $this->write($messages, count($messages) === 1 || $newline);
}
// messages can be an array, let's convert it to string anyway
$messages = join($newline ? "\n" : '', (array) $messages);

@ -105,6 +105,7 @@ class Installer
protected $dryRun = false;
protected $verbose = false;
protected $update = false;
protected $dumpAutoloader = true;
protected $runScripts = true;
protected $ignorePlatformReqs = false;
protected $preferStable = false;
@ -317,15 +318,17 @@ class Installer
}
}
// write autoloader
if ($this->optimizeAutoloader) {
$this->io->write('<info>Generating optimized autoload files</info>');
} else {
$this->io->write('<info>Generating autoload files</info>');
}
if ($this->dumpAutoloader) {
// write autoloader
if ($this->optimizeAutoloader) {
$this->io->write('<info>Generating optimized autoload files</info>');
} else {
$this->io->write('<info>Generating autoload files</info>');
}
$this->autoloadGenerator->setDevMode($this->devMode);
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
$this->autoloadGenerator->setDevMode($this->devMode);
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
}
if ($this->runScripts) {
// dispatch post event
@ -1163,6 +1166,21 @@ class Installer
return $this;
}
/**
* set whether to run autoloader or not
*
* @param boolean $dumpAutoloader
* @return Installer
*/
public function setDumpAutoloader($dumpAutoloader = true)
{
$this->dumpAutoloader = (boolean) $dumpAutoloader;
return $this;
}
/**
* set whether to run scripts or not
*

@ -61,7 +61,7 @@ class PluginInstaller extends LibraryInstaller
}
parent::install($repo, $package);
$this->composer->getPluginManager()->registerPackage($package);
$this->composer->getPluginManager()->registerPackage($package, true);
}
/**
@ -75,6 +75,6 @@ class PluginInstaller extends LibraryInstaller
}
parent::update($repo, $initial, $target);
$this->composer->getPluginManager()->registerPackage($target);
$this->composer->getPluginManager()->registerPackage($target, true);
}
}

@ -154,8 +154,7 @@ class JsonFile
if ($schema === self::LAX_SCHEMA) {
$schemaData->additionalProperties = true;
$schemaData->properties->name->required = false;
$schemaData->properties->description->required = false;
$schemaData->required = array();
}
$validator = new Validator();

@ -21,10 +21,10 @@ class JsonValidationException extends Exception
{
protected $errors;
public function __construct($message, $errors = array())
public function __construct($message, $errors = array(), \Exception $previous = null)
{
$this->errors = $errors;
parent::__construct($message);
parent::__construct($message, 0, $previous);
}
public function getErrors()

@ -78,6 +78,6 @@ class MultiConstraint implements LinkConstraintInterface
$constraints[] = $constraint->__toString();
}
return '['.implode($this->conjunctive ? ', ' : ' | ', $constraints).']';
return '['.implode($this->conjunctive ? ' ' : ' || ', $constraints).']';
}
}

@ -224,7 +224,7 @@ class ArrayLoader implements LoaderInterface
*/
public function getBranchAlias(array $config)
{
if ('dev-' !== substr($config['version'], 0, 4)
if (('dev-' !== substr($config['version'], 0, 4) && '-dev' !== substr($config['version'], -4))
|| !isset($config['extra']['branch-alias'])
|| !is_array($config['extra']['branch-alias'])
) {
@ -248,6 +248,14 @@ class ArrayLoader implements LoaderInterface
continue;
}
// If using numeric aliases ensure the alias is a valid subversion
if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
&& ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch))
&& (stripos($targetPrefix, $sourcePrefix) !== 0)
) {
continue;
}
return $validatedTargetBranch;
}
}

@ -131,7 +131,7 @@ class RootPackageLoader extends ArrayLoader
$minimumStability = $stabilities[$minimumStability];
foreach ($requires as $reqName => $reqVersion) {
// parse explicit stability flags to the most unstable
if (preg_match('{^[^,\s]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) {
if (preg_match('{^[^@]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) {
$name = strtolower($reqName);
$stability = $stabilities[VersionParser::normalizeStability($match[1])];

@ -251,6 +251,17 @@ class ValidatingArrayLoader implements LoaderInterface
if ('-dev' !== substr($validatedTargetBranch, -4)) {
$this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must be a parseable number like 2.0-dev';
unset($this->config['extra']['branch-alias'][$sourceBranch]);
continue;
}
// If using numeric aliases ensure the alias is a valid subversion
if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch))
&& ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch))
&& (stripos($targetPrefix, $sourcePrefix) !== 0)
) {
$this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') is not a valid numeric alias for this version';
unset($this->config['extra']['branch-alias'][$sourceBranch]);
}
}
}

@ -169,6 +169,22 @@ class VersionParser
throw new \UnexpectedValueException('Invalid version string "'.$version.'"'.$extraMessage);
}
/**
* Extract numeric prefix from alias, if it is in numeric format, suitable for
* version comparison
*
* @param string $branch Branch name (e.g. 2.1.x-dev)
* @return string|false Numeric prefix if present (e.g. 2.1.) or false
*/
public function parseNumericAliasPrefix($branch)
{
if (preg_match('/^(?P<version>(\d+\\.)*\d+)(?:\.x)?-dev$/i', $branch, $matches)) {
return $matches['version'].".";
}
return false;
}
/**
* Normalizes a branch name to be able to perform comparisons on it
*

@ -190,10 +190,11 @@ class PluginManager
* instead for BC
*
* @param PackageInterface $package
* @param bool $failOnMissingClasses By default this silently skips plugins that can not be found, but if set to true it fails with an exception
*
* @throws \UnexpectedValueException
*/
public function registerPackage(PackageInterface $package)
public function registerPackage(PackageInterface $package, $failOnMissingClasses = false)
{
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
@ -242,10 +243,12 @@ class PluginManager
if ($oldInstallerPlugin) {
$installer = new $class($this->io, $this->composer);
$this->composer->getInstallationManager()->addInstaller($installer);
} else {
} elseif (class_exists($class)) {
$plugin = new $class();
$this->addPlugin($plugin);
$this->registeredPlugins[] = $package->getName();
} elseif ($failOnMissingClasses) {
throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class);
}
}
}

@ -53,8 +53,6 @@ class ComposerRepository extends ArrayRepository
protected $eventDispatcher;
protected $sourceMirrors;
protected $distMirrors;
private $rawData;
private $minimalPackages;
private $degradedMode = false;
private $rootData;
@ -206,6 +204,11 @@ class ComposerRepository extends ArrayRepository
$this->loadProviderListings($this->loadRootServerFile());
}
if ($this->lazyProvidersUrl) {
// Can not determine list of provided packages for lazy repositories
return array();
}
if ($this->providersUrl) {
return array_keys($this->providerListing);
}

@ -160,6 +160,7 @@ class PearRepository extends ArrayRepository
$package = new CompletePackage($composerPackageName, $normalizedVersion, $version);
$package->setType('pear-library');
$package->setDescription($packageDefinition->getDescription());
$package->setLicense(array($packageDefinition->getLicense()));
$package->setDistType('file');
$package->setDistUrl($distUrl);
$package->setAutoload(array('classmap' => array('')));

@ -33,7 +33,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
*/
public function initialize()
{
preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
$this->originUrl = 'bitbucket.org';
@ -143,7 +143,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
*/
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
{
if (!preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) {
if (!preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) {
return false;
}

@ -202,7 +202,7 @@ class GitDriver extends VcsDriver
$this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) {
if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match)) {
$branches[$match[1]] = $match[2];
}
}
@ -241,7 +241,11 @@ class GitDriver extends VcsDriver
return false;
}
// TODO try to connect to the server
$process = new ProcessExecutor($io);
if($process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url)) === 0) {
return true;
}
return false;
}
}

@ -33,7 +33,7 @@ class HgBitbucketDriver extends VcsDriver
*/
public function initialize()
{
preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
$this->originUrl = 'bitbucket.org';
@ -153,7 +153,7 @@ class HgBitbucketDriver extends VcsDriver
*/
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
{
if (!preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) {
if (!preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) {
return false;
}

@ -169,6 +169,9 @@ class Git
if (getenv('GIT_WORK_TREE')) {
putenv('GIT_WORK_TREE');
}
// clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
putenv("DYLD_LIBRARY_PATH");
}
public static function getGitHubDomainsRegex(Config $config)

@ -26,7 +26,6 @@ class RemoteFilesystem
{
private $io;
private $config;
private $firstCall;
private $bytesMax;
private $originUrl;
private $fileUrl;
@ -344,7 +343,7 @@ class RemoteFilesystem
{
if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
$message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit');
$gitHubUtil = new GitHub($this->io, $this->config, null, $this);
$gitHubUtil = new GitHub($this->io, $this->config, null);
if (!$gitHubUtil->authorizeOAuth($this->originUrl)
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
) {

@ -67,6 +67,17 @@ class AutoloadGeneratorTest extends TestCase
*/
private $eventDispatcher;
/**
* Map of setting name => return value configuration for the stub Config
* object.
*
* Note: must be public for compatibility with PHP 5.3 runtimes where
* closures cannot access private members of the classes they are created
* in.
* @var array
*/
public $configValueMap;
protected function setUp()
{
$this->fs = new Filesystem;
@ -79,18 +90,23 @@ class AutoloadGeneratorTest extends TestCase
$this->config = $this->getMock('Composer\Config');
$this->config->expects($this->at(0))
->method('get')
->with($this->equalTo('vendor-dir'))
->will($this->returnCallback(function () use ($that) {
$this->configValueMap = array(
'vendor-dir' => function () use ($that) {
return $that->vendorDir;
}));
},
);
$this->config->expects($this->at(1))
$this->config->expects($this->atLeastOnce())
->method('get')
->with($this->equalTo('vendor-dir'))
->will($this->returnCallback(function () use ($that) {
return $that->vendorDir;
->will($this->returnCallback(function ($arg) use ($that) {
$ret = null;
if (isset($that->configValueMap[$arg])) {
$ret = $that->configValueMap[$arg];
if (is_callable($ret)) {
$ret = $ret();
}
}
return $ret;
}));
$this->origDir = getcwd();
@ -483,6 +499,48 @@ class AutoloadGeneratorTest extends TestCase
include $this->vendorDir.'/composer/autoload_classmap.php'
);
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
$this->assertNotContains('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php'));
}
public function testClassMapAutoloadingAuthoritative()
{
$package = new Package('a', '1.0', '1.0');
$packages = array();
$packages[] = $a = new Package('a/a', '1.0', '1.0');
$packages[] = $b = new Package('b/b', '1.0', '1.0');
$packages[] = $c = new Package('c/c', '1.0', '1.0');
$a->setAutoload(array('classmap' => array('')));
$b->setAutoload(array('classmap' => array('test.php')));
$c->setAutoload(array('classmap' => array('./')));
$this->repository->expects($this->once())
->method('getCanonicalPackages')
->will($this->returnValue($packages));
$this->configValueMap['classmap-authoritative'] = true;
$this->fs->ensureDirectoryExists($this->vendorDir.'/composer');
$this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src');
$this->fs->ensureDirectoryExists($this->vendorDir.'/b/b');
$this->fs->ensureDirectoryExists($this->vendorDir.'/c/c/foo');
file_put_contents($this->vendorDir.'/a/a/src/a.php', '<?php class ClassMapFoo {}');
file_put_contents($this->vendorDir.'/b/b/test.php', '<?php class ClassMapBar {}');
file_put_contents($this->vendorDir.'/c/c/foo/test.php', '<?php class ClassMapBaz {}');
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_7');
$this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated.");
$this->assertEquals(
array(
'ClassMapBar' => $this->vendorDir.'/b/b/test.php',
'ClassMapBaz' => $this->vendorDir.'/c/c/foo/test.php',
'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php',
),
include $this->vendorDir.'/composer/autoload_classmap.php'
);
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
$this->assertContains('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php'));
}
public function testFilesAutoloadGeneration()
@ -829,10 +887,7 @@ EOF;
->method('getCanonicalPackages')
->will($this->returnValue(array()));
$this->config->expects($this->at(2))
->method('get')
->with($this->equalTo('use-include-path'))
->will($this->returnValue(true));
$this->configValueMap['use-include-path'] = true;
$this->fs->ensureDirectoryExists($this->vendorDir.'/a');

@ -97,6 +97,18 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
),
);
$data['incorrect local config does not cause ErrorException'] = array(
array(
'packagist' => array('type' => 'composer', 'url' => 'https?://packagist.org', 'allow_ssl_downgrade' => true),
'type' => 'vcs',
'url' => 'http://example.com',
),
array(
'type' => 'vcs',
'url' => 'http://example.com',
),
);
return $data;
}
@ -121,6 +133,35 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($home.'/foo', $config->get('cache-dir'));
}
public function testRealpathReplacement()
{
$config = new Config(false, '/foo/bar');
$config->merge(array('config' => array(
'bin-dir' => '$HOME/foo',
'cache-dir' => '/baz/',
'vendor-dir' => 'vendor'
)));
$home = rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '\\/');
$this->assertEquals('/foo/bar/vendor', $config->get('vendor-dir'));
$this->assertEquals($home.'/foo', $config->get('bin-dir'));
$this->assertEquals('/baz', $config->get('cache-dir'));
}
public function testFetchingRelativePaths()
{
$config = new Config(false, '/foo/bar');
$config->merge(array('config' => array(
'bin-dir' => '{$vendor-dir}/foo',
'vendor-dir' => 'vendor'
)));
$this->assertEquals('/foo/bar/vendor', $config->get('vendor-dir'));
$this->assertEquals('/foo/bar/vendor/foo', $config->get('bin-dir'));
$this->assertEquals('vendor', $config->get('vendor-dir', Config::RELATIVE_PATHS));
$this->assertEquals('vendor/foo', $config->get('bin-dir', Config::RELATIVE_PATHS));
}
public function testOverrideGithubProtocols()
{
$config = new Config(false);

@ -49,6 +49,30 @@ class ConsoleIOTest extends TestCase
$consoleIO->write('some information about something', false);
}
public function testWriteWithMultipleLineStringWhenDebugging()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->once())
->method('write')
->with(
$this->callback(function($messages){
$result = preg_match("[(.*)/(.*) First line]", $messages[0]) > 0;
$result &= preg_match("[(.*)/(.*) Second line]", $messages[1]) > 0;
return $result;
}),
$this->equalTo(false)
);
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$startTime = microtime(true);
$consoleIO->enableDebugging($startTime);
$example = explode('\n', 'First line\nSecond lines');
$consoleIO->write($example, false);
}
public function testOverwrite()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
@ -58,21 +82,27 @@ class ConsoleIOTest extends TestCase
->method('write')
->with($this->equalTo('something (<question>strlen = 23</question>)'));
$outputMock->expects($this->at(1))
->method('isDecorated')
->willReturn(true);
$outputMock->expects($this->at(2))
->method('write')
->with($this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false));
$outputMock->expects($this->at(2))
$outputMock->expects($this->at(3))
->method('write')
->with($this->equalTo('shorter (<comment>12</comment>)'), $this->equalTo(false));
$outputMock->expects($this->at(3))
$outputMock->expects($this->at(4))
->method('write')
->with($this->equalTo(str_repeat(' ', 11)), $this->equalTo(false));
$outputMock->expects($this->at(4))
$outputMock->expects($this->at(5))
->method('write')
->with($this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false));
$outputMock->expects($this->at(5))
$outputMock->expects($this->at(6))
->method('isDecorated')
->willReturn(true);
$outputMock->expects($this->at(7))
->method('write')
->with($this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false));
$outputMock->expects($this->at(6))
$outputMock->expects($this->at(8))
->method('write')
->with($this->equalTo('something longer than initial (<info>34</info>)'));

@ -241,10 +241,10 @@ class InstallerTest extends TestCase
}
$installationManager = $composer->getInstallationManager();
$this->assertSame($expect, implode("\n", $installationManager->getTrace()));
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
if ($expectOutput) {
$this->assertEquals($expectOutput, $output);
$this->assertEquals(rtrim($expectOutput), rtrim($output));
}
}
@ -258,21 +258,7 @@ class InstallerTest extends TestCase
continue;
}
$test = file_get_contents($file->getRealpath());
$content = '(?:.(?!--[A-Z]))+';
$pattern = '{^
--TEST--\s*(?P<test>.*?)\s*
(?:--CONDITION--\s*(?P<condition>'.$content.'))?\s*
--COMPOSER--\s*(?P<composer>'.$content.')\s*
(?:--LOCK--\s*(?P<lock>'.$content.'))?\s*
(?:--INSTALLED--\s*(?P<installed>'.$content.'))?\s*
--RUN--\s*(?P<run>.*?)\s*
(?:--EXPECT-LOCK--\s*(?P<expectLock>'.$content.'))?\s*
(?:--EXPECT-OUTPUT--\s*(?P<expectOutput>'.$content.'))?\s*
(?:--EXPECT-EXIT-CODE--\s*(?P<expectExitCode>\d+))?\s*
--EXPECT--\s*(?P<expect>.*?)\s*
$}xs';
$testData = $this->readTestFile($file, $fixturesDir);
$installed = array();
$installedDev = array();
@ -280,48 +266,44 @@ class InstallerTest extends TestCase
$expectLock = array();
$expectExitCode = 0;
if (preg_match($pattern, $test, $match)) {
try {
$message = $match['test'];
$condition = !empty($match['condition']) ? $match['condition'] : null;
$composer = JsonFile::parseJson($match['composer']);
try {
$message = $testData['TEST'];
$condition = !empty($testData['CONDITION']) ? $testData['CONDITION'] : null;
$composer = JsonFile::parseJson($testData['COMPOSER']);
if (isset($composer['repositories'])) {
foreach ($composer['repositories'] as &$repo) {
if ($repo['type'] !== 'composer') {
continue;
}
// Change paths like file://foobar to file:///path/to/fixtures
if (preg_match('{^file://[^/]}', $repo['url'])) {
$repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7);
}
unset($repo);
if (isset($composer['repositories'])) {
foreach ($composer['repositories'] as &$repo) {
if ($repo['type'] !== 'composer') {
continue;
}
}
if (!empty($match['lock'])) {
$lock = JsonFile::parseJson($match['lock']);
if (!isset($lock['hash'])) {
$lock['hash'] = md5(json_encode($composer));
// Change paths like file://foobar to file:///path/to/fixtures
if (preg_match('{^file://[^/]}', $repo['url'])) {
$repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7);
}
unset($repo);
}
if (!empty($match['installed'])) {
$installed = JsonFile::parseJson($match['installed']);
}
$run = $match['run'];
if (!empty($match['expectLock'])) {
$expectLock = JsonFile::parseJson($match['expectLock']);
}
if (!empty($testData['LOCK'])) {
$lock = JsonFile::parseJson($testData['LOCK']);
if (!isset($lock['hash'])) {
$lock['hash'] = md5(json_encode($composer));
}
$expectOutput = $match['expectOutput'];
$expect = $match['expect'];
$expectExitCode = (int) $match['expectExitCode'];
} catch (\Exception $e) {
die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file)));
}
} else {
die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file)));
if (!empty($testData['INSTALLED'])) {
$installed = JsonFile::parseJson($testData['INSTALLED']);
}
$run = $testData['RUN'];
if (!empty($testData['EXPECT-LOCK'])) {
$expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']);
}
$expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null;
$expect = $testData['EXPECT'];
$expectExitCode = isset($testData['EXPECT-EXIT-CODE']) ? (int) $testData['EXPECT-EXIT-CODE'] : 0;
} catch (\Exception $e) {
die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file)));
}
$tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode);
@ -329,4 +311,59 @@ class InstallerTest extends TestCase
return $tests;
}
protected function readTestFile(\SplFileInfo $file, $fixturesDir)
{
$tokens = preg_split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file->getRealPath()), null, PREG_SPLIT_DELIM_CAPTURE);
$sectionInfo = array(
'TEST' => true,
'CONDITION' => false,
'COMPOSER' => true,
'LOCK' => false,
'INSTALLED' => false,
'RUN' => true,
'EXPECT-LOCK' => false,
'EXPECT-OUTPUT' => false,
'EXPECT-EXIT-CODE' => false,
'EXPECT' => true,
);
$section = null;
foreach ($tokens as $i => $token)
{
if (null === $section && empty($token)) {
continue; // skip leading blank
}
if (null === $section) {
if (!isset($sectionInfo[$token])) {
throw new \RuntimeException(sprintf(
'The test file "%s" must not contain a section named "%s".',
str_replace($fixturesDir.'/', '', $file),
$token
));
}
$section = $token;
continue;
}
$sectionData = $token;
$data[$section] = $sectionData;
$section = $sectionData = null;
}
foreach ($sectionInfo as $section => $required) {
if ($required && !isset($data[$section])) {
throw new \RuntimeException(sprintf(
'The test file "%s" must have a section named "%s".',
str_replace($fixturesDir.'/', '', $file),
$section
));
}
}
return $data;
}
}

@ -22,9 +22,9 @@ use Composer\IO\IOInterface;
class FactoryMock extends Factory
{
public static function createConfig(IOInterface $io = null)
public static function createConfig(IOInterface $io = null, $cwd = null)
{
$config = new Config();
$config = new Config(true, $cwd);
$config->merge(array(
'config' => array('home' => sys_get_temp_dir().'/composer-test'),

@ -138,6 +138,50 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Composer\Package\AliasPackage', $package);
$this->assertEquals('1.0.x-dev', $package->getPrettyVersion());
$config = array(
'name' => 'A',
'version' => 'dev-master',
'extra' => array('branch-alias' => array('dev-master' => '1.0-dev')),
);
$package = $this->loader->load($config);
$this->assertInstanceOf('Composer\Package\AliasPackage', $package);
$this->assertEquals('1.0.x-dev', $package->getPrettyVersion());
$config = array(
'name' => 'B',
'version' => '4.x-dev',
'extra' => array('branch-alias' => array('4.x-dev' => '4.0.x-dev')),
);
$package = $this->loader->load($config);
$this->assertInstanceOf('Composer\Package\AliasPackage', $package);
$this->assertEquals('4.0.x-dev', $package->getPrettyVersion());
$config = array(
'name' => 'B',
'version' => '4.x-dev',
'extra' => array('branch-alias' => array('4.x-dev' => '4.0-dev')),
);
$package = $this->loader->load($config);
$this->assertInstanceOf('Composer\Package\AliasPackage', $package);
$this->assertEquals('4.0.x-dev', $package->getPrettyVersion());
$config = array(
'name' => 'C',
'version' => '4.x-dev',
'extra' => array('branch-alias' => array('4.x-dev' => '3.4.x-dev')),
);
$package = $this->loader->load($config);
$this->assertInstanceOf('Composer\Package\CompletePackage', $package);
$this->assertEquals('4.x-dev', $package->getPrettyVersion());
}
public function testAbandoned()

@ -143,6 +143,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase
'foo/bar' => '~2.1.0-beta2',
'bar/baz' => '1.0.x-dev as 1.2.0',
'qux/quux' => '1.0.*@rc',
'zux/complex' => '~1.0,>=1.0.2@dev'
),
'minimum-stability' => 'alpha',
));
@ -151,6 +152,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(
'bar/baz' => BasePackage::STABILITY_DEV,
'qux/quux' => BasePackage::STABILITY_RC,
'zux/complex' => BasePackage::STABILITY_DEV,
), $package->getStabilityFlags());
}
}

@ -140,6 +140,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase
'branch-alias' => array(
'dev-master' => '2.0-dev',
'dev-old' => '1.0.x-dev',
'3.x-dev' => '3.1.x-dev'
),
),
'bin' => array(
@ -324,6 +325,34 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase
),
false
),
array(
array(
'name' => 'foo/bar',
'extra' => array(
'branch-alias' => array(
'5.x-dev' => '3.1.x-dev'
),
)
),
array(
'extra.branch-alias.5.x-dev : the target branch (3.1.x-dev) is not a valid numeric alias for this version'
),
false
),
array(
array(
'name' => 'foo/bar',
'extra' => array(
'branch-alias' => array(
'5.x-dev' => '3.1-dev'
),
)
),
array(
'extra.branch-alias.5.x-dev : the target branch (3.1-dev) is not a valid numeric alias for this version'
),
false
),
);
}
}

@ -67,6 +67,29 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
return array_map($createPackage, $data);
}
/**
* @dataProvider numericAliasVersions
*/
public function testParseNumericAliasPrefix($input, $expected)
{
$parser = new VersionParser;
$this->assertSame($expected, $parser->parseNumericAliasPrefix($input));
}
public function numericAliasVersions()
{
return array(
array('0.x-dev', '0.'),
array('1.0.x-dev', '1.0.'),
array('1.x-dev', '1.'),
array('1.2.x-dev', '1.2.'),
array('1.2-dev', '1.2.'),
array('1-dev', '1.'),
array('dev-develop', false),
array('dev-master', false),
);
}
/**
* @dataProvider successfulNormalizedVersions
*/

@ -113,8 +113,12 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
array('3.1.2-dev', true, 'dev', '3.1.2-dev'),
// dev packages with alias inherit the alias
array('dev-master', true, 'dev', '~2.1@dev', '2.1.x-dev'),
array('dev-master', true, 'dev', '~2.1@dev', '2.1-dev'),
array('dev-master', true, 'dev', '~2.1@dev', '2.1.3.x-dev'),
array('dev-master', true, 'dev', '~2.0@dev', '2.x-dev'),
// numeric alias
array('3.x-dev', true, 'dev', '~3.0@dev', '3.0.x-dev'),
array('3.x-dev', true, 'dev', '~3.0@dev', '3.0-dev'),
);
}

@ -19,6 +19,14 @@ use Composer\Package\BasePackage;
class ArtifactRepositoryTest extends TestCase
{
public function setUp()
{
parent::setUp();
if (!extension_loaded('zip')) {
$this->markTestSkipped('You need the zip extension to run this test.');
}
}
public function testExtractsConfigsFromZipArchives()
{
$expectedPackages = array(

@ -121,6 +121,7 @@ class ChannelReaderTest extends TestCase
$expectedPackage->setType('pear-library');
$expectedPackage->setDistType('file');
$expectedPackage->setDescription('description');
$expectedPackage->setLicense(array('license'));
$expectedPackage->setDistUrl("http://test.loc/get/sample-1.0.0.1.tgz");
$expectedPackage->setAutoload(array('classmap' => array('')));
$expectedPackage->setIncludePaths(array('/'));

@ -84,13 +84,6 @@ class PearRepositoryTest extends TestCase
public function repositoryDataProvider()
{
return array(
array(
'pear.phpunit.de',
array(
array('name' => 'pear-pear.phpunit.de/PHPUnit_MockObject', 'version' => '1.1.1'),
array('name' => 'pear-pear.phpunit.de/PHPUnit', 'version' => '3.6.10'),
)
),
array(
'pear.php.net',
array(

Loading…
Cancel
Save