1
0
Fork 0

Merge branch 'master' of git://github.com/composer/composer

Conflicts:
	src/Composer/Repository/PearRepository.php
pull/658/head
Bastian Hofmann 2012-05-06 14:42:13 +02:00
commit 4b3fc2b5fa
64 changed files with 1281 additions and 416 deletions

View File

@ -3,10 +3,17 @@
* Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev * Schema: Added 'require-dev' for development-time requirements (tests, etc), install with --dev
* Schema: Removed 'recommend' * Schema: Removed 'recommend'
* Schema: 'suggest' is now informational and can use any description for a package, not only a constraint * Schema: 'suggest' is now informational and can use any description for a package, not only a constraint
* Break: vendor/.composer/autoload.php has been moved to vendor/autoload.php, other files are now in vendor/composer/
* Added caching of repository metadata (faster startup times & failover if packagist is down) * Added caching of repository metadata (faster startup times & failover if packagist is down)
* Added removal of packages that are not needed anymore
* Added include_path support for legacy projects that are full of require_once statements * Added include_path support for legacy projects that are full of require_once statements
* Added installation notifications API to allow better statistics on Composer repositories * Added installation notifications API to allow better statistics on Composer repositories
* Added autoloading support for root packages that use target-dir
* Added awareness of the root package presence and support for it's provide/replace/conflict keys
* Added IOInterface::isDecorated to test for colored output support
* Improved repository protocol to have large cacheable parts * Improved repository protocol to have large cacheable parts
* Fixed various bugs relating to package aliasing, proxy configuration, binaries
* Various bug fixes and docs improvements
* 1.0.0-alpha2 (2012-04-03) * 1.0.0-alpha2 (2012-04-03)

6
composer.lock generated
View File

@ -12,19 +12,19 @@
{ {
"package": "symfony/console", "package": "symfony/console",
"version": "dev-master", "version": "dev-master",
"source-reference": "8e3c42aa976f18a9bfcb0694553e5f99def3309c", "source-reference": "eaad4427b10ff39402bce0ae4f8cd1faf2b6532a",
"alias": "2.1.9999999.9999999-dev" "alias": "2.1.9999999.9999999-dev"
}, },
{ {
"package": "symfony/finder", "package": "symfony/finder",
"version": "dev-master", "version": "dev-master",
"source-reference": "57ec7198a70e6c40e450ba66cc2f8ecab98746c8", "source-reference": "78b2e33951821b6d423718f57788f1894dcb935a",
"alias": "2.1.9999999.9999999-dev" "alias": "2.1.9999999.9999999-dev"
}, },
{ {
"package": "symfony/process", "package": "symfony/process",
"version": "dev-master", "version": "dev-master",
"source-reference": "2e4da8c8076744bafed97451bb1574c96cda0e68", "source-reference": "718655f4bc664d693b33f3e6e8a895e454208021",
"alias": "2.1.9999999.9999999-dev" "alias": "2.1.9999999.9999999-dev"
} }
], ],

View File

@ -67,7 +67,7 @@ executable and invoke it without `php`.
### Using Composer ### Using Composer
Next, run the command the `install` command to resolve and download dependencies: Next, run the `install` command to resolve and download dependencies:
$ php composer.phar install $ php composer.phar install
@ -80,7 +80,7 @@ capable of autoloading all of the classes in any of the libraries that it
downloads. To use it, just add the following line to your code's bootstrap downloads. To use it, just add the following line to your code's bootstrap
process: process:
require 'vendor/.composer/autoload.php'; require 'vendor/autoload.php';
Woh! Now start using monolog! To keep learning more about Composer, keep Woh! Now start using monolog! To keep learning more about Composer, keep
reading the "Basic Usage" chapter. reading the "Basic Usage" chapter.

View File

@ -108,7 +108,7 @@ same version of the dependencies.
If no `composer.json` lock file exists, it will read the dependencies and If no `composer.json` lock file exists, it will read the dependencies and
versions from `composer.json` and create the lock file. versions from `composer.json` and create the lock file.
This means that if any of the dependencies get a new version, you won't get the updates. This means that if any of the dependencies get a new version, you won't get the updates
automatically. To update to the new version, use `update` command. This will fetch automatically. To update to the new version, use `update` command. This will fetch
the latest matching versions (according to your `composer.json` file) and also update the latest matching versions (according to your `composer.json` file) and also update
the lock file with the new version. the lock file with the new version.
@ -136,10 +136,10 @@ but it makes life quite a bit simpler.
## Autoloading ## Autoloading
For libraries that specify autoload information, Composer generates a For libraries that specify autoload information, Composer generates a
`vendor/.composer/autoload.php` file. You can simply include this file and you `vendor/autoload.php` file. You can simply include this file and you
will get autoloading for free. will get autoloading for free.
require 'vendor/.composer/autoload.php'; require 'vendor/autoload.php';
This makes it really easy to use third party code. For example: If your This makes it really easy to use third party code. For example: If your
project depends on monolog, you can just start using classes from it, and they project depends on monolog, you can just start using classes from it, and they
@ -168,13 +168,13 @@ be in your project root. An example filename would be `src/Acme/Foo.php`
containing an `Acme\Foo` class. 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 `install` to re-generate
the `vendor/.composer/autoload.php` file. the `vendor/autoload.php` file.
Including that file will also return the autoloader instance, so you can store Including that file will also return the autoloader instance, so you can store
the return value of the include call in a variable and add more namespaces. the return value of the include call in a variable and add more namespaces.
This can be useful for autoloading classes in a test suite, for example. This can be useful for autoloading classes in a test suite, for example.
$loader = require 'vendor/.composer/autoload.php'; $loader = require 'vendor/autoload.php';
$loader->add('Acme\Test', __DIR__); $loader->add('Acme\Test', __DIR__);
In addition to PSR-0 autoloading, classmap is also supported. This allows In addition to PSR-0 autoloading, classmap is also supported. This allows
@ -182,7 +182,7 @@ classes to be autoloaded even if they do not conform to PSR-0. See the
[autoload reference](04-schema.md#autoload) for more details. [autoload reference](04-schema.md#autoload) for more details.
> **Note:** Composer provides its own autoloader. If you don't want to use > **Note:** Composer provides its own autoloader. If you don't want to use
that one, you can just include `vendor/.composer/autoload_namespaces.php`, that one, you can just include `vendor/autoload_namespaces.php`,
which returns an associative array mapping namespaces to directories. which returns an associative array mapping namespaces to directories.
← [Intro](00-intro.md) | [Libraries](02-libraries.md) → ← [Intro](00-intro.md) | [Libraries](02-libraries.md) →

View File

@ -62,20 +62,29 @@ Here are a few examples of valid tag names:
For every branch, a package development version will be created. If the branch For every branch, a package development version will be created. If the branch
name looks like a version, the version will be `{branchname}-dev`. For example name looks like a version, the version will be `{branchname}-dev`. For example
a branch `2.0` will get a version `2.0-dev`. If the branch does not look like a branch `2.0` will get a version `2.0.x-dev` (the `.x` is added for technical
a version, it will be `dev-{branchname}`. `master` results in a `dev-master` reasons, to make sure it is recognized as a branch, a `2.0.x` branch would also
version. be valid and be turned into `2.0.x-dev` as well. 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: Here are some examples of version branch names:
1.0 1.x
1.* 1.0 (equals 1.0.x)
1.1.x 1.1.x
1.1.*
> **Note:** When you install a dev version, it will install it from source. > **Note:** When you install a dev version, it will install it from source.
See [Repositories](05-repositories.md) for more information. See [Repositories](05-repositories.md) for more information.
### Aliases
It is possible alias branch names to versions. For example, you could alias
`dev-master` to `1.0.x-dev`, which would allow you to require `1.0.x-dev` in all
the packages.
See [Aliases](articles/aliases.md) for more information.
## Lock file ## Lock file
For your library you may commit the `composer.lock` file if you want to. This For your library you may commit the `composer.lock` file if you want to. This

View File

@ -199,11 +199,6 @@ directory other than `vendor`.
By setting this option you can change the `bin` ([Vendor Bins](articles/vendor-bins.md)) By setting this option you can change the `bin` ([Vendor Bins](articles/vendor-bins.md))
directory to something other than `vendor/bin`. directory to something other than `vendor/bin`.
### COMPOSER_PROCESS_TIMEOUT
This env var controls the time composer waits for commands (such as git
commands) to finish executing. The default value is 60 seconds.
### http_proxy or HTTP_PROXY ### http_proxy or HTTP_PROXY
If you are using composer from behind an HTTP proxy, you can use the standard If you are using composer from behind an HTTP proxy, you can use the standard
@ -215,4 +210,19 @@ some tools like git or curl will only use the lower-cased `http_proxy` version.
Alternatively you can also define the git proxy using Alternatively you can also define the git proxy using
`git config --global http.proxy <proxy url>`. `git config --global http.proxy <proxy url>`.
### COMPOSER_HOME
The `COMPOSER_HOME` var allows you to change the composer home directory. This
is a hidden, global (per-user on the machine) directory that is shared between
all projects.
By default it points to `/home/<user>/.composer` on *nix,
`/Users/<user>/.composer` on OSX and
`C:\Users\<user>\AppData\Roaming\Composer` on Windows.
### COMPOSER_PROCESS_TIMEOUT
This env var controls the time composer waits for commands (such as git
commands) to finish executing. The default value is 300 seconds (5 minutes).
&larr; [Libraries](02-libraries.md) | [Schema](04-schema.md) &rarr; &larr; [Libraries](02-libraries.md) | [Schema](04-schema.md) &rarr;

View File

@ -145,6 +145,7 @@ Each author object can have following properties:
* **name:** The author's name. Usually his real name. * **name:** The author's name. Usually his real name.
* **email:** The author's email address. * **email:** The author's email address.
* **homepage:** An URL to the author's website. * **homepage:** An URL to the author's website.
* **role:** The authors' role in the project (e.g. developer or translator)
An example: An example:
@ -153,12 +154,14 @@ An example:
{ {
"name": "Nils Adermann", "name": "Nils Adermann",
"email": "naderman@naderman.de", "email": "naderman@naderman.de",
"homepage": "http://www.naderman.de" "homepage": "http://www.naderman.de",
"role": "Developer"
}, },
{ {
"name": "Jordi Boggiano", "name": "Jordi Boggiano",
"email": "j.boggiano@seld.be", "email": "j.boggiano@seld.be",
"homepage": "http://seld.be" "homepage": "http://seld.be",
"role": "Developer"
} }
] ]
} }
@ -215,21 +218,26 @@ Example:
Autoload mapping for a PHP autoloader. Autoload mapping for a PHP autoloader.
Currently [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) Currently [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
autoloading and classmap generation are supported. autoloading and classmap generation are supported. PSR-0 is the recommended way though
since it offers greater flexibility (no need to regenerate the autoloader when you add
classes).
Under the `psr-0` key you define a mapping from namespaces to paths, relative to the Under the `psr-0` key you define a mapping from namespaces to paths, relative to the
package root. package root. Note that this also supports the PEAR-style convention.
Example: Example:
{ {
"autoload": { "autoload": {
"psr-0": { "Monolog": "src/" } "psr-0": {
"Monolog": "src/",
"Vendor\\Namespace": "src/",
"Pear_Style": "src/"
}
} }
} }
Optional, but it is highly recommended that you follow PSR-0 and use this. If you need to search for a same prefix in multiple directories,
If you need to search for a same namespace prefix in multiple directories,
you can specify them as an array as such: you can specify them as an array as such:
{ {
@ -238,15 +246,24 @@ you can specify them as an array as such:
} }
} }
If you want to have a fallback directory where any namespace can be, you can
use an empty prefix like:
{
"autoload": {
"psr-0": { "": "src/" }
}
}
You can use the classmap generation support to define autoloading for all libraries You can use the classmap generation support to define autoloading for all libraries
that do not follow PSR-0. To configure this you specify all directories that do not follow PSR-0. To configure this you specify all directories or files
to search for classes. to search for classes.
Example: Example:
{ {
"autoload: { "autoload: {
"classmap": ["src/", "lib/"] "classmap": ["src/", "lib/", "Something.php"]
} }
} }
@ -368,6 +385,9 @@ The following options are supported:
* **process-timeout:** Defaults to `300`. The duration processes like git clones * **process-timeout:** Defaults to `300`. The duration processes like git clones
can run before Composer assumes they died out. You may need to make this can run before Composer assumes they died out. You may need to make this
higher if you have a slow connection or huge vendors. higher if you have a slow connection or huge vendors.
* **notify-on-install:** Defaults to `true`. Composer allows repositories to
define a notification URL, so that they get notified whenever a package from
that repository is installed. This option allows you to disable that behaviour.
Example: Example:

View File

@ -54,15 +54,24 @@ want to learn why.
### Composer ### Composer
The main repository type is the `composer` repository. It uses a single The main repository type is the `composer` repository. It uses a single
`packages.json` file that contains all of the package metadata. The JSON `packages.json` file that contains all of the package metadata.
format is as follows:
This is also the repository type that 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 `packagist.org`. For `example.org/packages.json` the
repository URL would be `example.org`.
#### packages
The only required field is `packages`. The JSON structure is as follows:
{ {
"packages": {
"vendor/packageName": { "vendor/packageName": {
"name": "vendor/packageName",
"description": "Package description",
"versions": {
"master-dev": { @composer.json }, "master-dev": { @composer.json },
"1.0.x-dev": { @composer.json },
"0.0.1": { @composer.json },
"1.0.0": { @composer.json } "1.0.0": { @composer.json }
} }
} }
@ -88,12 +97,54 @@ Here is a minimal package definition:
It may include any of the other fields specified in the [schema](04-schema.md). It may include any of the other fields specified in the [schema](04-schema.md).
The `composer` repository is also what packagist uses. To reference a #### notify
`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 `notify` field allows you to specify an URL template for a URL that will
the repository would be `http://packagist.org`. For be called every time a user installs a package.
`http://example.org/packages.json` the repository URL would be
`http://example.org`. An example value:
{
"notify": "/downloads/%package%"
}
For `example.org/packages.json` containing a `monolog/monolog` package, this
would send a `POST` request to `example.org/downloads/monolog/monolog` with
following parameters:
* **version:** The version of the package.
* **version_normalized:** The normalized internal representation of the
version.
This field is optional.
#### includes
For large repositories it is possible to split the `packages.json` into
multiple files. The `includes` field allows you to reference these additional
files.
An example:
{
"includes": {
"packages-2011.json": {
"sha1": "525a85fb37edd1ad71040d429928c2c0edec9d17"
},
"packages-2012-01.json": {
"sha1": "897cde726f8a3918faf27c803b336da223d400dd"
},
"packages-2012-02.json": {
"sha1": "26f911ad717da26bbcac3f8f435280d13917efa5"
}
}
}
The SHA-1 sum of the file allows it to be cached and only re-requested if the
hash changed.
This field is optional. You probably don't need it for your own custom
repository.
### VCS ### VCS

90
doc/articles/aliases.md Normal file
View File

@ -0,0 +1,90 @@
<!--
tagline: Alias branch names to versions
-->
# Aliases
## Why aliases?
When you are using a VCS repository, you will only get comparable versions for
branches that look like versions, such as `2.0`. For your `master` branch, you
will get a `dev-master` version. For your `bugfix` branch, you will get a
`dev-bugfix` version.
If your `master` branch is used to tag releases of the `1.0` development line,
i.e. `1.0.1`, `1.0.2`, `1.0.3`, etc., any package depending on it will
probably require version `1.0.*`.
If anyone wants to require the latest `dev-master`, they have a problem: Other
packages may require `1.0.*`, so requiring that dev version will lead to
conflicts, since `dev-master` does not match the `1.0.*` constraint.
Enter aliases.
## Branch alias
The `dev-master` branch is one in your main VCS repo. It is rather common that
someone will want the latest master dev version. Thus, Composer allows you to
alias your `dev-master` branch to a `1.0.x-dev` version. It is done by
specifying a `branch-alias` field under `extra` in `composer.json`:
{
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}
The branch version must begin with `dev-` (non-comparable version), the alias
must be a comparable dev version. 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, you can now require `1.0.*` and it will happily install
`dev-master` for you.
## Require inline alias
Branch aliases are great for aliasing main development lines. But in order to
use them you need to have control over the source repository, and you need to
commit changes to version control.
This is not really fun when you just want to try a bugfix of some library that
is a dependency of your local project.
For this reason, you can alias packages in your `require` and `require-dev`
fields. Let's say you found a bug in the `monolog/monolog` package. You cloned
Monolog on GitHub and fixed the issue in a branch named `bugfix`. Now you want
to install that version of monolog in your local project.
You are using `symfony/monolog-bundle` which requires `monolog/monolog` version
`1.*`. So you need your `dev-bugfix` to match that constraint.
Just add this to your project's root `composer.json`:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/you/monolog"
}
],
"require": {
"symfony/monolog-bundle": "2.0",
"monolog/monolog": "dev-bugfix as 1.0.x-dev"
}
}
That will fetch the `dev-bugfix` version of `monolog/monolog` from your GitHub
and alias it to `1.0.x-dev`.
> **Note:** If a package with inline aliases is required, the alias (right of
> the `as`) is used as the version constraint. The part left of the `as` is
> discarded. As a consequence, if A requires B and B requires `monolog/monolog`
> version `dev-bugfix as 1.0.x-dev`, installing A will make B require
> `1.0.x-dev`, which may exist as a branch alias or an actual `1.0` branch. If
> it does not, it must be re-inline-aliased in A's `composer.json`.
> **Note:** Inline aliasing should be avoided, especially for published
> packages. If you found a bug, try and get your fix merged upstream. This
> helps to avoid issues for users of your package.

View File

@ -5,7 +5,8 @@
Satis can be used to host the metadata of your company's private packages, or Satis can be used to host the metadata of your company's private packages, or
your own. It basically acts as a micro-packagist. You can get it from your own. It basically acts as a micro-packagist. You can get it from
[GitHub](http://github.com/composer/satis). [GitHub](http://github.com/composer/satis) or install via CLI:
`composer.phar create-project composer/satis`.
## Setup ## Setup
@ -13,12 +14,29 @@ For example let's assume you have a few packages you want to reuse across your
company but don't really want to open-source. You would first define a Satis company but don't really want to open-source. You would first define a Satis
configuration file, which is basically a stripped-down version of a configuration file, which is basically a stripped-down version of a
`composer.json` file. It contains a few repositories, and then you use the require `composer.json` file. It contains a few repositories, and then you use the require
key to say which packages it should dump in the static repository it creates. key to say which packages it should dump in the static repository it creates, or
use require-all to select all of them.
Here is an example configuration, you see that it holds a few VCS repositories, Here is an example configuration, you see that it holds a few VCS repositories,
but those could be any types of [repositories](../05-repositories.md). Then but those could be any types of [repositories](../05-repositories.md). Then it
the require just lists all the packages we need, using a `"*"` constraint to uses `"require-all": true` which selects all versions of all packages in the
make sure all versions are selected. repositories you defined.
{
"name": "My Repository",
"homepage": "http://packages.example.org",
"repositories": [
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo" },
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
{ "type": "vcs", "url": "http://github.com/mycompany/privaterepo2" }
],
"require-all": true
}
If you want to cherry pick which packages you want, you can list all the packages
you want to have in your satis repository inside the classic composer `require` key,
using a `"*"` constraint to make sure all versions are selected, or another
constraint if you want really specific versions.
{ {
"repositories": [ "repositories": [
@ -29,7 +47,7 @@ make sure all versions are selected.
"require": { "require": {
"company/package": "*", "company/package": "*",
"company/package2": "*", "company/package2": "*",
"company/package3": "*" "company/package3": "2.0.0"
} }
} }

View File

@ -66,6 +66,10 @@
"type": "string", "type": "string",
"description": "Homepage URL for the author.", "description": "Homepage URL for the author.",
"format": "uri" "format": "uri"
},
"role": {
"type": "string",
"description": "Author's role in the project."
} }
} }
} }

View File

@ -25,25 +25,26 @@ use Composer\Util\Filesystem;
*/ */
class AutoloadGenerator class AutoloadGenerator
{ {
public function dump(RepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir) public function dump(RepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $bcLinks = false)
{ {
$filesystem = new Filesystem(); $filesystem = new Filesystem();
$filesystem->ensureDirectoryExists($installationManager->getVendorPath()); $filesystem->ensureDirectoryExists($installationManager->getVendorPath());
$filesystem->ensureDirectoryExists($targetDir); $filesystem->ensureDirectoryExists($targetDir);
$vendorPath = strtr(realpath($installationManager->getVendorPath()), '\\', '/'); $vendorPath = strtr(realpath($installationManager->getVendorPath()), '\\', '/');
$relVendorPath = $filesystem->findShortestPath(getcwd(), $vendorPath, true); $relVendorPath = $filesystem->findShortestPath(getcwd(), $vendorPath, true);
$vendorDirCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); $vendorPathCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true);
$vendorPathToTargetDirCode = $filesystem->findShortestPathCode($vendorPath, realpath($targetDir), true);
$appBaseDir = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); $appBaseDirCode = $filesystem->findShortestPathCode($vendorPath, getcwd(), true);
$appBaseDir = str_replace('__DIR__', '$vendorDir', $appBaseDir); $appBaseDirCode = str_replace('__DIR__', '$vendorDir', $appBaseDirCode);
$namespacesFile = <<<EOF $namespacesFile = <<<EOF
<?php <?php
// autoload_namespace.php generated by Composer // autoload_namespace.php generated by Composer
\$vendorDir = $vendorDirCode; \$vendorDir = $vendorPathCode;
\$baseDir = $appBaseDir; \$baseDir = $appBaseDirCode;
return array( return array(
@ -55,22 +56,7 @@ EOF;
foreach ($autoloads['psr-0'] as $namespace => $paths) { foreach ($autoloads['psr-0'] as $namespace => $paths) {
$exportedPaths = array(); $exportedPaths = array();
foreach ($paths as $path) { foreach ($paths as $path) {
$path = strtr($path, '\\', '/'); $exportedPaths[] = $this->getPathCode($filesystem, $relVendorPath, $vendorPath, $path);
$baseDir = '';
if (!$filesystem->isAbsolutePath($path)) {
if (strpos($path, $relVendorPath) === 0) {
// path starts with vendor dir
$path = substr($path, strlen($relVendorPath));
$baseDir = '$vendorDir . ';
} else {
$path = '/'.$path;
$baseDir = '$baseDir . ';
}
} elseif (strpos($path, $vendorPath) === 0) {
$path = substr($path, strlen($vendorPath));
$baseDir = '$vendorDir . ';
}
$exportedPaths[] = $baseDir.var_export($path, true);
} }
$exportedPrefix = var_export($namespace, true); $exportedPrefix = var_export($namespace, true);
$namespacesFile .= " $exportedPrefix => "; $namespacesFile .= " $exportedPrefix => ";
@ -87,13 +73,44 @@ EOF;
// autoload_classmap.php generated by Composer // autoload_classmap.php generated by Composer
\$vendorDir = $vendorDirCode; \$vendorDir = $vendorPathCode;
\$baseDir = $appBaseDir; \$baseDir = $appBaseDirCode;
return array( return array(
EOF; EOF;
// add custom psr-0 autoloading if the root package has a target dir
$targetDirLoader = null;
$mainAutoload = $mainPackage->getAutoload();
if ($mainPackage->getTargetDir() && $mainAutoload['psr-0']) {
$levels = count(explode('/', trim(strtr($mainPackage->getTargetDir(), '\\', '/'), '/')));
$prefixes = implode(', ', array_map(function ($prefix) {
return var_export($prefix, true);
}, array_keys($mainAutoload['psr-0'])));
$baseDirFromVendorDirCode = $filesystem->findShortestPathCode($vendorPath, getcwd(), true);
$targetDirLoader = <<<EOF
spl_autoload_register(function(\$class) {
\$dir = $baseDirFromVendorDirCode . '/';
\$prefixes = array($prefixes);
foreach (\$prefixes as \$prefix) {
if (0 !== strpos(\$class, \$prefix)) {
continue;
}
\$path = \$dir . implode('/', array_slice(explode('\\\\', \$class), $levels)).'.php';
if (!stream_resolve_include_path(\$path)) {
return false;
}
require_once \$path;
return true;
}
});
EOF;
}
// flatten array // flatten array
$autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap']));
foreach ($autoloads['classmap'] as $dir) { foreach ($autoloads['classmap'] as $dir) {
@ -106,11 +123,23 @@ EOF;
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile); file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile); file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
if ($includePathFile = $this->getIncludePathsFile($packageMap)) { if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $relVendorPath, $vendorPath, $vendorPathCode, $appBaseDirCode)) {
file_put_contents($targetDir.'/include_paths.php', $includePathFile); file_put_contents($targetDir.'/include_paths.php', $includePathFile);
} }
file_put_contents($targetDir.'/autoload.php', $this->getAutoloadFile(true, true, (Boolean) $includePathFile)); file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, true, true, (Boolean) $includePathFile, $targetDirLoader));
copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
// TODO BC feature, add E_DEPRECATED in autoload.php on April 30th, remove after May 30th
if ($bcLinks) {
$filesystem->ensureDirectoryExists($vendorPath.'/.composer');
file_put_contents($vendorPath.'/.composer/autoload_namespaces.php', "<?php\n// Deprecated file, use the one in root of vendor dir\nreturn include dirname(__DIR__).'/composer/autoload_namespaces.php';\n");
file_put_contents($vendorPath.'/.composer/autoload_classmap.php', "<?php\n// Deprecated file, use the one in root of vendor dir\nreturn include dirname(__DIR__).'/composer/autoload_classmap.php';\n");
file_put_contents($vendorPath.'/.composer/autoload.php', "<?php\n// Deprecated file, use the one in root of vendor dir\nreturn include dirname(__DIR__).'/autoload.php';\n");
file_put_contents($vendorPath.'/.composer/ClassLoader.php', "<?php\n// Deprecated file, use the one in root of vendor dir\nreturn include dirname(__DIR__).'/composer/ClassLoader.php';\n");
if ($includePathFile) {
file_put_contents($vendorPath.'/.composer/include_paths.php', "<?php\n// Deprecated file, use the one in root of vendor dir\nreturn include dirname(__DIR__).'/composer/include_paths.php';\n");
}
}
} }
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
@ -186,7 +215,7 @@ EOF;
return $loader; return $loader;
} }
protected function getIncludePathsFile(array $packageMap) protected function getIncludePathsFile(array $packageMap, Filesystem $filesystem, $relVendorPath, $vendorPath, $vendorPathCode, $appBaseDirCode)
{ {
$includePaths = array(); $includePaths = array();
@ -198,6 +227,7 @@ EOF;
} }
foreach ($package->getIncludePaths() as $includePath) { foreach ($package->getIncludePaths() as $includePath) {
$includePath = trim($includePath, '/');
$includePaths[] = empty($installPath) ? $includePath : $installPath.'/'.$includePath; $includePaths[] = empty($installPath) ? $includePath : $installPath.'/'.$includePath;
} }
} }
@ -206,30 +236,65 @@ EOF;
return; return;
} }
return sprintf( $includePathsFile = <<<EOF
"<?php\nreturn %s;\n", var_export($includePaths, true) <?php
);
// include_paths.php generated by Composer
\$vendorDir = $vendorPathCode;
\$baseDir = $appBaseDirCode;
return array(
EOF;
foreach ($includePaths as $path) {
$includePathsFile .= " " . $this->getPathCode($filesystem, $relVendorPath, $vendorPath, $path) . ",\n";
} }
protected function getAutoloadFile($usePSR0, $useClassMap, $useIncludePath) return $includePathsFile . ");\n";
}
protected function getPathCode(Filesystem $filesystem, $relVendorPath, $vendorPath, $path)
{ {
$file = <<<'HEADER' $path = strtr($path, '\\', '/');
$baseDir = '';
if (!$filesystem->isAbsolutePath($path)) {
if (strpos($path, $relVendorPath) === 0) {
// path starts with vendor dir
$path = substr($path, strlen($relVendorPath));
$baseDir = '$vendorDir . ';
} else {
$path = '/'.$path;
$baseDir = '$baseDir . ';
}
} elseif (strpos($path, $vendorPath) === 0) {
$path = substr($path, strlen($vendorPath));
$baseDir = '$vendorDir . ';
}
return $baseDir.var_export($path, true);
}
protected function getAutoloadFile($vendorPathToTargetDirCode, $usePSR0, $useClassMap, $useIncludePath, $targetDirLoader)
{
$file = <<<HEADER
<?php <?php
// autoload.php generated by Composer // autoload.php generated by Composer
if (!class_exists('Composer\\Autoload\\ClassLoader', false)) { if (!class_exists('Composer\\\\Autoload\\\\ClassLoader', false)) {
require __DIR__.'/ClassLoader.php'; require $vendorPathToTargetDirCode . '/ClassLoader.php';
} }
return call_user_func(function() { return call_user_func(function() {
$loader = new \Composer\Autoload\ClassLoader(); \$loader = new \\Composer\\Autoload\\ClassLoader();
\$composerDir = $vendorPathToTargetDirCode;
HEADER; HEADER;
if ($useIncludePath) { if ($useIncludePath) {
$file .= <<<'INCLUDE_PATH' $file .= <<<'INCLUDE_PATH'
$includePaths = require __DIR__.'/include_paths.php'; $includePaths = require $composerDir . '/include_paths.php';
array_unshift($includePaths, get_include_path()); array_unshift($includePaths, get_include_path());
set_include_path(join(PATH_SEPARATOR, $includePaths)); set_include_path(join(PATH_SEPARATOR, $includePaths));
@ -239,7 +304,7 @@ INCLUDE_PATH;
if ($usePSR0) { if ($usePSR0) {
$file .= <<<'PSR0' $file .= <<<'PSR0'
$map = require __DIR__.'/autoload_namespaces.php'; $map = require $composerDir . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) { foreach ($map as $namespace => $path) {
$loader->add($namespace, $path); $loader->add($namespace, $path);
} }
@ -250,7 +315,7 @@ PSR0;
if ($useClassMap) { if ($useClassMap) {
$file .= <<<'CLASSMAP' $file .= <<<'CLASSMAP'
$classMap = require __DIR__.'/autoload_classmap.php'; $classMap = require $composerDir . '/autoload_classmap.php';
if ($classMap) { if ($classMap) {
$loader->addClassMap($classMap); $loader->addClassMap($classMap);
} }
@ -259,6 +324,8 @@ PSR0;
CLASSMAP; CLASSMAP;
} }
$file .= $targetDirLoader;
return $file . <<<'FOOTER' return $file . <<<'FOOTER'
$loader->register(); $loader->register();

View File

@ -18,6 +18,7 @@ use Composer\Installer\ProjectInstaller;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Repository\ComposerRepository; use Composer\Repository\ComposerRepository;
use Composer\Repository\FilesystemRepository; use Composer\Repository\FilesystemRepository;
use Composer\Repository\InstalledFilesystemRepository;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -113,7 +114,7 @@ EOT
$io->write('<info>Installing ' . $package->getName() . ' as new project.</info>', true); $io->write('<info>Installing ' . $package->getName() . ' as new project.</info>', true);
$projectInstaller = new ProjectInstaller($directory, $dm); $projectInstaller = new ProjectInstaller($directory, $dm);
$projectInstaller->install(new FilesystemRepository(new JsonFile('php://memory')), $package); $projectInstaller->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), $package);
$io->write('<info>Created project into directory ' . $directory . '</info>', true); $io->write('<info>Created project into directory ' . $directory . '</info>', true);
chdir($directory); chdir($directory);

View File

@ -35,7 +35,7 @@ class InitCommand extends Command
public function parseAuthorString($author) public function parseAuthorString($author)
{ {
if (preg_match('/^(?P<name>[- \.,\w\']+) <(?P<email>.+?)>$/u', $author, $match)) { if (preg_match('/^(?P<name>[- \.,\w\']+) <(?P<email>.+?)>$/u', $author, $match)) {
if (!function_exists('filter_var') || $match['email'] === filter_var($match['email'], FILTER_VALIDATE_EMAIL)) { if (!function_exists('filter_var') || version_compare(PHP_VERSION, '5.3.3', '<') || $match['email'] === filter_var($match['email'], FILTER_VALIDATE_EMAIL)) {
return array( return array(
'name' => trim($match['name']), 'name' => trim($match['name']),
'email' => $match['email'] 'email' => $match['email']

View File

@ -53,7 +53,7 @@ EOT
$rfs->copy('getcomposer.org', $remoteFilename, $tempFilename); $rfs->copy('getcomposer.org', $remoteFilename, $tempFilename);
try { try {
chmod($tempFilename, 0755); chmod($tempFilename, 0777 & ~umask());
// test the phar validity // test the phar validity
$phar = new \Phar($tempFilename); $phar = new \Phar($tempFilename);
// free the variable to unlock the file // free the variable to unlock the file

View File

@ -81,10 +81,10 @@ class Compiler
$this->addFile($phar, $file); $this->addFile($phar, $file);
} }
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/ClassLoader.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_namespaces.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_classmap.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/ClassLoader.php'));
$this->addComposerBin($phar); $this->addComposerBin($phar);
// Stubs // Stubs

View File

@ -67,6 +67,10 @@ class Application extends BaseApplication
$this->registerCommands(); $this->registerCommands();
$this->io = new ConsoleIO($input, $output, $this->getHelperSet()); $this->io = new ConsoleIO($input, $output, $this->getHelperSet());
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$output->writeln('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
}
return parent::doRun($input, $output); return parent::doRun($input, $output);
} }

View File

@ -69,6 +69,8 @@ class DefaultPolicy implements PolicyInterface
$literals = $this->pruneToBestVersion($literals); $literals = $this->pruneToBestVersion($literals);
$literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals); $literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals);
$literals = $this->pruneRemoteAliases($literals);
} }
$selected = call_user_func_array('array_merge', $packages); $selected = call_user_func_array('array_merge', $packages);
@ -239,4 +241,38 @@ class DefaultPolicy implements PolicyInterface
return $selected; return $selected;
} }
/**
* Assumes that locally aliased (in root package requires) packages take priority over branch-alias ones
*
* If no package is a local alias, nothing happens
*/
protected function pruneRemoteAliases(array $literals)
{
$hasLocalAlias = false;
foreach ($literals as $literal) {
$package = $literal->getPackage();
if ($package instanceof AliasPackage && $package->isRootPackageAlias()) {
$hasLocalAlias = true;
break;
}
}
if (!$hasLocalAlias) {
return $literals;
}
$selected = array();
foreach ($literals as $literal) {
$package = $literal->getPackage();
if ($package instanceof AliasPackage && $package->isRootPackageAlias()) {
$selected[] = $literal;
}
}
return $selected;
}
} }

View File

@ -24,6 +24,7 @@ use Composer\Util\Filesystem;
class DownloadManager class DownloadManager
{ {
private $preferSource = false; private $preferSource = false;
private $filesystem;
private $downloaders = array(); private $downloaders = array();
/** /**
@ -31,9 +32,10 @@ class DownloadManager
* *
* @param Boolean $preferSource prefer downloading from source * @param Boolean $preferSource prefer downloading from source
*/ */
public function __construct($preferSource = false) public function __construct($preferSource = false, Filesystem $filesystem = null)
{ {
$this->preferSource = $preferSource; $this->preferSource = $preferSource;
$this->filesystem = $filesystem ?: new Filesystem();
} }
/** /**
@ -135,8 +137,7 @@ class DownloadManager
throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
} }
$fs = new Filesystem(); $this->filesystem->ensureDirectoryExists($targetDir);
$fs->ensureDirectoryExists($targetDir);
$downloader = $this->getDownloaderForInstalledPackage($package); $downloader = $this->getDownloaderForInstalledPackage($package);
$downloader->download($package, $targetDir); $downloader->download($package, $targetDir);

View File

@ -61,7 +61,7 @@ class GitDownloader extends VcsDownloader
*/ */
protected function enforceCleanDirectory($path) protected function enforceCleanDirectory($path)
{ {
$command = sprintf('cd %s && git status --porcelain', escapeshellarg($path)); $command = sprintf('cd %s && git status --porcelain --untracked-files=no', escapeshellarg($path));
if (0 !== $this->process->execute($command, $output)) { if (0 !== $this->process->execute($command, $output)) {
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
} }

View File

@ -38,6 +38,14 @@ class Factory
} }
} }
// Protect directory against web access
if (!file_exists($home . '/.htaccess')) {
if (!is_dir($home)) {
@mkdir($home, 0777, true);
}
@file_put_contents($home . '/.htaccess', 'Deny from all');
}
$config = new Config(); $config = new Config();
$file = new JsonFile($home.'/config.json'); $file = new JsonFile($home.'/config.json');
@ -151,8 +159,25 @@ class Factory
protected function addLocalRepository(RepositoryManager $rm, $vendorDir) protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
{ {
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); // TODO BC feature, remove after May 30th
$rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed_dev.json'))); if (file_exists($vendorDir.'/.composer/installed.json')) {
if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); }
rename($vendorDir.'/.composer/installed.json', $vendorDir.'/composer/installed.json');
}
if (file_exists($vendorDir.'/.composer/installed_dev.json')) {
if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); }
rename($vendorDir.'/.composer/installed_dev.json', $vendorDir.'/composer/installed_dev.json');
}
if (file_exists($vendorDir.'/installed.json')) {
if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); }
rename($vendorDir.'/installed.json', $vendorDir.'/composer/installed.json');
}
if (file_exists($vendorDir.'/installed_dev.json')) {
if (!is_dir($vendorDir.'/composer')) { mkdir($vendorDir.'/composer/', 0777, true); }
rename($vendorDir.'/installed_dev.json', $vendorDir.'/composer/installed_dev.json');
}
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json')));
$rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed_dev.json')));
} }
protected function addPackagistRepository(array $localConfig) protected function addPackagistRepository(array $localConfig)

View File

@ -55,6 +55,14 @@ class ConsoleIO implements IOInterface
return $this->input->isInteractive(); return $this->input->isInteractive();
} }
/**
* {@inheritDoc}
*/
public function isDecorated()
{
return $this->output->isDecorated();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -33,6 +33,13 @@ interface IOInterface
*/ */
function isVerbose(); function isVerbose();
/**
* Is this output decorated?
*
* @return Boolean
*/
function isDecorated();
/** /**
* Writes a message to the output. * Writes a message to the output.
* *

View File

@ -35,6 +35,14 @@ class NullIO implements IOInterface
return false; return false;
} }
/**
* {@inheritDoc}
*/
public function isDecorated()
{
return false;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -27,6 +27,7 @@ use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Locker; use Composer\Package\Locker;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Repository\ArrayRepository;
use Composer\Repository\CompositeRepository; use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository; use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
@ -76,6 +77,11 @@ class Installer
*/ */
protected $eventDispatcher; protected $eventDispatcher;
/**
* @var AutoloadGenerator
*/
protected $autoloadGenerator;
protected $preferSource = false; protected $preferSource = false;
protected $devMode = false; protected $devMode = false;
protected $dryRun = false; protected $dryRun = false;
@ -102,8 +108,9 @@ class Installer
* @param Locker $locker * @param Locker $locker
* @param InstallationManager $installationManager * @param InstallationManager $installationManager
* @param EventDispatcher $eventDispatcher * @param EventDispatcher $eventDispatcher
* @param AutoloadGenerator $autoloadGenerator
*/ */
public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher) public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, AutoloadGenerator $autoloadGenerator)
{ {
$this->io = $io; $this->io = $io;
$this->package = $package; $this->package = $package;
@ -112,6 +119,7 @@ class Installer
$this->locker = $locker; $this->locker = $locker;
$this->installationManager = $installationManager; $this->installationManager = $installationManager;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->autoloadGenerator = $autoloadGenerator;
} }
/** /**
@ -128,7 +136,13 @@ class Installer
} }
// create installed repo, this contains all local packages + platform packages (php & extensions) // create installed repo, this contains all local packages + platform packages (php & extensions)
$repos = array_merge($this->repositoryManager->getLocalRepositories(), array(new PlatformRepository())); $repos = array_merge(
$this->repositoryManager->getLocalRepositories(),
array(
new ArrayRepository(array($this->package)),
new PlatformRepository(),
)
);
$installedRepo = new CompositeRepository($repos); $installedRepo = new CompositeRepository($repos);
if ($this->additionalInstalledRepository) { if ($this->additionalInstalledRepository) {
$installedRepo->addRepository($this->additionalInstalledRepository); $installedRepo->addRepository($this->additionalInstalledRepository);
@ -152,10 +166,12 @@ class Installer
} }
} }
// dump suggestions // output suggestions
foreach ($this->suggestedPackages as $suggestion) { foreach ($this->suggestedPackages as $suggestion) {
if (!$installedRepo->findPackages($suggestion['target'])) {
$this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')');
} }
}
if (!$this->dryRun) { if (!$this->dryRun) {
// write lock // write lock
@ -172,9 +188,8 @@ class Installer
// write autoloader // write autoloader
$this->io->write('<info>Generating autoload files</info>'); $this->io->write('<info>Generating autoload files</info>');
$generator = new AutoloadGenerator;
$localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories());
$generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); $this->autoloadGenerator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath() . '/composer', true);
// dispatch post event // dispatch post event
$eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
@ -186,6 +201,11 @@ class Installer
protected function doInstall($localRepo, $installedRepo, $aliases, $devMode = false) protected function doInstall($localRepo, $installedRepo, $aliases, $devMode = false)
{ {
// initialize locker to create aliased packages
if (!$this->update && $this->locker->isLocked($devMode)) {
$lockedPackages = $this->locker->getLockedPackages($devMode);
}
// creating repository pool // creating repository pool
$pool = new Pool; $pool = new Pool;
$pool->addRepository($installedRepo); $pool->addRepository($installedRepo);
@ -196,6 +216,10 @@ class Installer
// creating requirements request // creating requirements request
$installFromLock = false; $installFromLock = false;
$request = new Request($pool); $request = new Request($pool);
$constraint = new VersionConstraint('=', $this->package->getVersion());
$request->install($this->package->getName(), $constraint);
if ($this->update) { if ($this->update) {
$this->io->write('<info>Updating '.($devMode ? 'dev ': '').'dependencies</info>'); $this->io->write('<info>Updating '.($devMode ? 'dev ': '').'dependencies</info>');
@ -214,7 +238,7 @@ class Installer
$this->io->write('<warning>Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies</warning>'); $this->io->write('<warning>Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies</warning>');
} }
foreach ($this->locker->getLockedPackages($devMode) as $package) { foreach ($lockedPackages as $package) {
$version = $package->getVersion(); $version = $package->getVersion();
foreach ($aliases as $alias) { foreach ($aliases as $alias) {
if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) {
@ -378,14 +402,16 @@ class Installer
foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) { foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) {
$package->setAlias($alias['alias_normalized']); $package->setAlias($alias['alias_normalized']);
$package->setPrettyAlias($alias['alias']); $package->setPrettyAlias($alias['alias']);
$package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']));
$aliasPackage->setRootPackageAlias(true);
} }
foreach ($this->repositoryManager->getLocalRepositories() as $repo) { foreach ($this->repositoryManager->getLocalRepositories() as $repo) {
foreach ($repo->findPackages($alias['package'], $alias['version']) as $package) { foreach ($repo->findPackages($alias['package'], $alias['version']) as $package) {
$package->setAlias($alias['alias_normalized']); $package->setAlias($alias['alias_normalized']);
$package->setPrettyAlias($alias['alias']); $package->setPrettyAlias($alias['alias']);
$package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']));
$package->getRepository()->removePackage($package); $package->getRepository()->removePackage($package);
$aliasPackage->setRootPackageAlias(true);
} }
} }
} }
@ -399,11 +425,13 @@ class Installer
* @param IOInterface $io * @param IOInterface $io
* @param Composer $composer * @param Composer $composer
* @param EventDispatcher $eventDispatcher * @param EventDispatcher $eventDispatcher
* @param AutoloadGenerator $autoloadGenerator
* @return Installer * @return Installer
*/ */
static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher = null) static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher = null, AutoloadGenerator $autoloadGenerator = null)
{ {
$eventDispatcher = $eventDispatcher ?: new EventDispatcher($composer, $io); $eventDispatcher = $eventDispatcher ?: new EventDispatcher($composer, $io);
$autoloadGenerator = $autoloadGenerator ?: new AutoloadGenerator;
return new static( return new static(
$io, $io,
@ -412,7 +440,8 @@ class Installer
$composer->getRepositoryManager(), $composer->getRepositoryManager(),
$composer->getLocker(), $composer->getLocker(),
$composer->getInstallationManager(), $composer->getInstallationManager(),
$eventDispatcher $eventDispatcher,
$autoloadGenerator
); );
} }

View File

@ -127,11 +127,7 @@ class InstallationManager
*/ */
public function install(RepositoryInterface $repo, InstallOperation $operation) public function install(RepositoryInterface $repo, InstallOperation $operation)
{ {
$package = $operation->getPackage(); $package = $this->antiAlias($operation->getPackage());
if ($package instanceof AliasPackage) {
$package = $package->getAliasOf();
$package->setInstalledAsAlias(true);
}
$installer = $this->getInstaller($package->getType()); $installer = $this->getInstaller($package->getType());
$installer->install($repo, $package); $installer->install($repo, $package);
$this->notifyInstall($package); $this->notifyInstall($package);
@ -145,15 +141,8 @@ class InstallationManager
*/ */
public function update(RepositoryInterface $repo, UpdateOperation $operation) public function update(RepositoryInterface $repo, UpdateOperation $operation)
{ {
$initial = $operation->getInitialPackage(); $initial = $this->antiAlias($operation->getInitialPackage());
if ($initial instanceof AliasPackage) { $target = $this->antiAlias($operation->getTargetPackage());
$initial = $initial->getAliasOf();
}
$target = $operation->getTargetPackage();
if ($target instanceof AliasPackage) {
$target = $target->getAliasOf();
$target->setInstalledAsAlias(true);
}
$initialType = $initial->getType(); $initialType = $initial->getType();
$targetType = $target->getType(); $targetType = $target->getType();
@ -176,10 +165,7 @@ class InstallationManager
*/ */
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
{ {
$package = $operation->getPackage(); $package = $this->antiAlias($operation->getPackage());
if ($package instanceof AliasPackage) {
$package = $package->getAliasOf();
}
$installer = $this->getInstaller($package->getType()); $installer = $this->getInstaller($package->getType());
$installer->uninstall($repo, $package); $installer->uninstall($repo, $package);
} }
@ -211,10 +197,23 @@ class InstallationManager
return getcwd().DIRECTORY_SEPARATOR.$this->vendorPath; return getcwd().DIRECTORY_SEPARATOR.$this->vendorPath;
} }
protected function notifyInstall(PackageInterface $package) private function notifyInstall(PackageInterface $package)
{ {
if ($package->getRepository() instanceof NotifiableRepositoryInterface) { if ($package->getRepository() instanceof NotifiableRepositoryInterface) {
$package->getRepository()->notifyInstall($package); $package->getRepository()->notifyInstall($package);
} }
} }
private function antiAlias(PackageInterface $package)
{
if ($package instanceof AliasPackage) {
$alias = $package;
$package = $package->getAliasOf();
$package->setInstalledAsAlias(true);
$package->setAlias($alias->getVersion());
$package->setPrettyAlias($alias->getPrettyVersion());
}
return $package;
}
} }

View File

@ -152,7 +152,7 @@ class LibraryInstaller implements InstallerInterface
// likely leftover from a previous install, make sure // likely leftover from a previous install, make sure
// that the target is still executable in case this // that the target is still executable in case this
// is a fresh install of the vendor. // is a fresh install of the vendor.
chmod($link, 0755); chmod($link, 0777 & ~umask());
} }
$this->io->write('Skipped installation of '.$bin.' for package '.$package->getName().', name conflicts with an existing file'); $this->io->write('Skipped installation of '.$bin.' for package '.$package->getName().', name conflicts with an existing file');
continue; continue;
@ -163,21 +163,24 @@ class LibraryInstaller implements InstallerInterface
// add unixy support for cygwin and similar environments // add unixy support for cygwin and similar environments
if ('.bat' !== substr($bin, -4)) { if ('.bat' !== substr($bin, -4)) {
file_put_contents($link, $this->generateUnixyProxyCode($bin, $link)); file_put_contents($link, $this->generateUnixyProxyCode($bin, $link));
chmod($link, 0755); chmod($link, 0777 & ~umask());
$link .= '.bat'; $link .= '.bat';
} }
file_put_contents($link, $this->generateWindowsProxyCode($bin, $link)); file_put_contents($link, $this->generateWindowsProxyCode($bin, $link));
} else { } else {
$cwd = getcwd();
try { try {
// under linux symlinks are not always supported for example // under linux symlinks are not always supported for example
// when using it in smbfs mounted folder // when using it in smbfs mounted folder
symlink($bin, $link); $relativeBin = $this->filesystem->findShortestPath($link, $bin);
chdir(dirname($link));
symlink($relativeBin, $link);
} catch (\ErrorException $e) { } catch (\ErrorException $e) {
file_put_contents($link, $this->generateUnixyProxyCode($bin, $link)); file_put_contents($link, $this->generateUnixyProxyCode($bin, $link));
} }
chdir($cwd);
} }
chmod($link, 0755); chmod($link, 0777 & ~umask());
} }
} }
@ -186,7 +189,7 @@ class LibraryInstaller implements InstallerInterface
if (!$package->getBinaries()) { if (!$package->getBinaries()) {
return; return;
} }
foreach ($package->getBinaries() as $bin => $os) { foreach ($package->getBinaries() as $bin) {
$link = $this->binDir.'/'.basename($bin); $link = $this->binDir.'/'.basename($bin);
if (!file_exists($link)) { if (!file_exists($link)) {
continue; continue;

View File

@ -237,7 +237,7 @@ class JsonFile
} }
} else { } else {
// Collapse empty {} and [] // Collapse empty {} and []
$result = rtrim($result); $result = rtrim($result)."\n\n".$indentStr;
} }
} }

View File

@ -27,6 +27,7 @@ class AliasPackage extends BasePackage
protected $prettyVersion; protected $prettyVersion;
protected $dev; protected $dev;
protected $aliasOf; protected $aliasOf;
protected $rootPackageAlias = false;
protected $requires; protected $requires;
protected $conflicts; protected $conflicts;
@ -146,6 +147,27 @@ class AliasPackage extends BasePackage
return $this->devRequires; return $this->devRequires;
} }
/**
* Stores whether this is an alias created by an aliasing in the requirements of the root package or not
*
* Use by the policy for sorting manually aliased packages first, see #576
*
* @param Boolean $value
*/
public function setRootPackageAlias($value)
{
return $this->rootPackageAlias = $value;
}
/**
* @see setRootPackageAlias
* @return Boolean
*/
public function isRootPackageAlias()
{
return $this->rootPackageAlias;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -222,6 +244,14 @@ class AliasPackage extends BasePackage
{ {
return $this->aliasOf->getScripts(); return $this->aliasOf->getScripts();
} }
public function setAliases(array $aliases)
{
return $this->aliasOf->setAliases($aliases);
}
public function getAliases()
{
return $this->aliasOf->getAliases();
}
public function getLicense() public function getLicense()
{ {
return $this->aliasOf->getLicense(); return $this->aliasOf->getLicense();

View File

@ -58,7 +58,10 @@ class ArrayLoader
$package->setExtra($config['extra']); $package->setExtra($config['extra']);
} }
if (isset($config['bin']) && is_array($config['bin'])) { if (isset($config['bin'])) {
if (!is_array($config['bin'])) {
throw new \UnexpectedValueException('Package '.$config['name'].'\'s bin key should be an array, '.gettype($config['bin']).' given.');
}
foreach ($config['bin'] as $key => $bin) { foreach ($config['bin'] as $key => $bin) {
$config['bin'][$key]= ltrim($bin, '/'); $config['bin'][$key]= ltrim($bin, '/');
} }
@ -166,6 +169,11 @@ class ArrayLoader
} }
if (isset($config['suggest']) && is_array($config['suggest'])) { if (isset($config['suggest']) && is_array($config['suggest'])) {
foreach ($config['suggest'] as $target => $reason) {
if ('self.version' === trim($reason)) {
$config['suggest'][$target] = $package->getPrettyVersion();
}
}
$package->setSuggests($config['suggest']); $package->setSuggests($config['suggest']);
} }

View File

@ -14,6 +14,8 @@ namespace Composer\Package\Loader;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryManager;
use Composer\Util\ProcessExecutor;
use Composer\Package\AliasPackage;
/** /**
* ArrayLoader built for the sole purpose of loading the root package * ArrayLoader built for the sole purpose of loading the root package
@ -25,10 +27,12 @@ use Composer\Repository\RepositoryManager;
class RootPackageLoader extends ArrayLoader class RootPackageLoader extends ArrayLoader
{ {
private $manager; private $manager;
private $process;
public function __construct(RepositoryManager $manager, VersionParser $parser = null) public function __construct(RepositoryManager $manager, VersionParser $parser = null, ProcessExecutor $process = null)
{ {
$this->manager = $manager; $this->manager = $manager;
$this->process = $process ?: new ProcessExecutor();
parent::__construct($parser); parent::__construct($parser);
} }
@ -38,7 +42,20 @@ class RootPackageLoader extends ArrayLoader
$config['name'] = '__root__'; $config['name'] = '__root__';
} }
if (!isset($config['version'])) { if (!isset($config['version'])) {
$config['version'] = '1.0.0'; $version = '1.0.0';
// try to fetch current version from git branch
if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) {
foreach ($this->process->splitLines($output) as $branch) {
if ($branch && preg_match('{^(?:\* ) *(?:[^/ ]+?/)?(\S+) *[a-f0-9]+ .*$}', $branch, $match)) {
$version = 'dev-'.$match[1];
}
}
}
$config['version'] = $version;
} else {
$version = $config['version'];
} }
$package = parent::load($config); $package = parent::load($config);
@ -76,6 +93,16 @@ class RootPackageLoader extends ArrayLoader
$package->setRepositories($config['repositories']); $package->setRepositories($config['repositories']);
} }
if (isset($config['extra']['branch-alias'][$version])
&& substr($config['extra']['branch-alias'][$version], -4) === '-dev'
) {
$targetBranch = $config['extra']['branch-alias'][$version];
$normalized = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4));
$version = preg_replace('{(\.9{7})+}', '.x', $normalized);
return new AliasPackage($package, $normalized, $version);
}
return $package; return $package;
} }
} }

View File

@ -90,12 +90,22 @@ class Locker
foreach ($lockedPackages as $info) { foreach ($lockedPackages as $info) {
$resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version']; $resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version'];
// try to find the package in the local repo (best match)
$package = $repo->findPackage($info['package'], $resolvedVersion); $package = $repo->findPackage($info['package'], $resolvedVersion);
// try to find the package in any repo
if (!$package) { if (!$package) {
$package = $this->repositoryManager->findPackage($info['package'], $resolvedVersion);
}
// try to find the package in any repo (second pass without alias + rebuild alias since it disappeared)
if (!$package && !empty($info['alias'])) {
$package = $this->repositoryManager->findPackage($info['package'], $info['version']); $package = $this->repositoryManager->findPackage($info['package'], $info['version']);
if ($package && !empty($info['alias'])) { if ($package) {
$package = new AliasPackage($package, $info['alias'], $info['alias']); $alias = new AliasPackage($package, $info['alias'], $info['alias']);
$package->getRepository()->addPackage($alias);
$package = $alias;
} }
} }

View File

@ -25,6 +25,13 @@ class ArrayRepository implements RepositoryInterface
{ {
protected $packages; protected $packages;
public function __construct(array $packages = array())
{
foreach ($packages as $package) {
$this->addPackage($package);
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -40,7 +40,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
$repoConfig['url'] = 'http://'.$repoConfig['url']; $repoConfig['url'] = 'http://'.$repoConfig['url'];
} }
$repoConfig['url'] = rtrim($repoConfig['url'], '/'); $repoConfig['url'] = rtrim($repoConfig['url'], '/');
if (function_exists('filter_var') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { if (function_exists('filter_var') && version_compare(PHP_VERSION, '5.3.3', '>=') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) {
throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']); throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']);
} }
@ -70,7 +70,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
array( array(
'method' => 'POST', 'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded', 'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($params), 'content' => http_build_query($params, '', '&'),
'timeout' => 3, 'timeout' => 3,
) )
); );

View File

@ -39,7 +39,7 @@ class PearRepository extends ArrayRepository
$repoConfig['url'] = 'http://'.$repoConfig['url']; $repoConfig['url'] = 'http://'.$repoConfig['url'];
} }
if (function_exists('filter_var') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) { if (function_exists('filter_var') && version_compare(PHP_VERSION, '5.3.3', '>=') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) {
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']); throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']);
} }
@ -74,7 +74,7 @@ class PearRepository extends ArrayRepository
} }
$this->addPackage($loader->load($rev)); $this->addPackage($loader->load($rev));
if ($this->io->isVerbose()) { if ($this->io->isVerbose()) {
$this->io->write('Loaded '. $data['name'].' '. $data['version']); $this->io->write('Loaded '.$rev['name'].' '.$rev['version']);
} }
} }
} }

View File

@ -27,20 +27,14 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
protected $rootIdentifier; protected $rootIdentifier;
protected $infoCache = array(); protected $infoCache = array();
public function __construct($url, IOInterface $io)
{
preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
parent::__construct($url, $io);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function initialize() public function initialize()
{ {
preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
} }
/** /**

View File

@ -28,11 +28,6 @@ class GitDriver extends VcsDriver
protected $repoDir; protected $repoDir;
protected $infoCache = array(); protected $infoCache = array();
public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
{
parent::__construct($url, $io, $process);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -41,11 +36,13 @@ class GitDriver extends VcsDriver
if (static::isLocalUrl($this->url)) { if (static::isLocalUrl($this->url)) {
$this->repoDir = str_replace('file://', '', $this->url); $this->repoDir = str_replace('file://', '', $this->url);
} else { } else {
$this->repoDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; $this->repoDir = $this->config->get('home') . '/cache.git/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/';
// update the repo if it is a valid git repository // update the repo if it is a valid git repository
if (is_dir($this->repoDir) && 0 === $this->process->execute('git remote', $output, $this->repoDir)) { if (is_dir($this->repoDir) && 0 === $this->process->execute('git remote', $output, $this->repoDir)) {
$this->process->execute('git remote update --prune origin', $output, $this->repoDir); if (0 !== $this->process->execute('git remote update --prune origin', $output, $this->repoDir)) {
$this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')</error>');
}
} else { } else {
// clean up directory and do a fresh clone into it // clean up directory and do a fresh clone into it
$fs = new Filesystem(); $fs = new Filesystem();

View File

@ -38,28 +38,15 @@ class GitHubDriver extends VcsDriver
*/ */
protected $gitDriver; protected $gitDriver;
/**
* Constructor
*
* @param string $url
* @param IOInterface $io
* @param ProcessExecutor $process
* @param RemoteFilesystem $remoteFilesystem
*/
public function __construct($url, IOInterface $io, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null)
{
preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
parent::__construct($url, $io, $process, $remoteFilesystem);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function initialize() public function initialize()
{ {
preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
$this->fetchRootIdentifier(); $this->fetchRootIdentifier();
} }
@ -248,6 +235,7 @@ class GitHubDriver extends VcsDriver
$this->gitDriver = new GitDriver( $this->gitDriver = new GitDriver(
$this->generateSshUrl(), $this->generateSshUrl(),
$this->io, $this->io,
$this->config,
$this->process, $this->process,
$this->remoteFilesystem $this->remoteFilesystem
); );

View File

@ -27,20 +27,14 @@ class HgBitbucketDriver extends VcsDriver
protected $rootIdentifier; protected $rootIdentifier;
protected $infoCache = array(); protected $infoCache = array();
public function __construct($url, IOInterface $io)
{
preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
parent::__construct($url, $io);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function initialize() public function initialize()
{ {
preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
$this->owner = $match[1];
$this->repository = $match[2];
} }
/** /**

View File

@ -26,24 +26,21 @@ class HgDriver extends VcsDriver
protected $rootIdentifier; protected $rootIdentifier;
protected $infoCache = array(); protected $infoCache = array();
public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
{
$this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/';
parent::__construct($url, $io, $process);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function initialize() public function initialize()
{ {
$url = escapeshellarg($this->url); $this->tmpDir = $this->config->get('home') . '/cache.hg/' . preg_replace('{[^a-z0-9]}i', '-', $this->url) . '/';
$tmpDir = escapeshellarg($this->tmpDir);
if (is_dir($this->tmpDir)) { if (is_dir($this->tmpDir)) {
$this->process->execute(sprintf('cd %s && hg pull -u', $tmpDir), $output); $this->process->execute(sprintf('cd %s && hg pull -u', escapeshellarg($this->tmpDir)), $output);
} else { } else {
$this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg(sys_get_temp_dir()), $url, $tmpDir), $output); $dir = dirname($this->tmpDir);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$this->process->execute(sprintf('cd %s && hg clone %s %s', escapeshellarg($dir), escapeshellarg($this->url), escapeshellarg($this->tmpDir)), $output);
} }
$this->getTags(); $this->getTags();

View File

@ -33,25 +33,7 @@ class SvnDriver extends VcsDriver
/** /**
* @var \Composer\Util\Svn * @var \Composer\Util\Svn
*/ */
protected $util; private $util;
/**
* @param string $url
* @param IOInterface $io
* @param ProcessExecutor $process
*
* @return $this
*/
public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
{
$url = self::normalizeUrl($url);
parent::__construct($this->baseUrl = rtrim($url, '/'), $io, $process);
if (false !== ($pos = strrpos($url, '/trunk'))) {
$this->baseUrl = substr($url, 0, $pos);
}
$this->util = new SvnUtil($this->baseUrl, $io, $this->process);
}
/** /**
* Execute an SVN command and try to fix up the process with credentials * Execute an SVN command and try to fix up the process with credentials
@ -64,6 +46,10 @@ class SvnDriver extends VcsDriver
*/ */
protected function execute($command, $url) protected function execute($command, $url)
{ {
if (null === $this->util) {
$this->util = new SvnUtil($this->baseUrl, $this->io, $this->process);
}
try { try {
return $this->util->execute($command, $url); return $this->util->execute($command, $url);
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
@ -78,6 +64,12 @@ class SvnDriver extends VcsDriver
*/ */
public function initialize() public function initialize()
{ {
$this->url = $this->baseUrl = rtrim(self::normalizeUrl($this->url), '/');
if (false !== ($pos = strrpos($this->url, '/trunk'))) {
$this->baseUrl = substr($this->url, 0, $pos);
}
$this->getBranches(); $this->getBranches();
$this->getTags(); $this->getTags();
} }

View File

@ -13,6 +13,7 @@
namespace Composer\Repository\Vcs; namespace Composer\Repository\Vcs;
use Composer\Downloader\TransportException; use Composer\Downloader\TransportException;
use Composer\Config;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem; use Composer\Util\RemoteFilesystem;
@ -26,6 +27,7 @@ abstract class VcsDriver implements VcsDriverInterface
{ {
protected $url; protected $url;
protected $io; protected $io;
protected $config;
protected $process; protected $process;
protected $remoteFilesystem; protected $remoteFilesystem;
@ -34,13 +36,15 @@ abstract class VcsDriver implements VcsDriverInterface
* *
* @param string $url The URL * @param string $url The URL
* @param IOInterface $io The IO instance * @param IOInterface $io The IO instance
* @param Config $config The composer configuration
* @param ProcessExecutor $process Process instance, injectable for mocking * @param ProcessExecutor $process Process instance, injectable for mocking
* @param callable $remoteFilesystem Remote Filesystem, injectable for mocking * @param callable $remoteFilesystem Remote Filesystem, injectable for mocking
*/ */
public function __construct($url, IOInterface $io, ProcessExecutor $process = null, $remoteFilesystem = null) final public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null, $remoteFilesystem = null)
{ {
$this->url = $url; $this->url = $url;
$this->io = $io; $this->io = $io;
$this->config = $config;
$this->process = $process ?: new ProcessExecutor; $this->process = $process ?: new ProcessExecutor;
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io);
} }
@ -58,7 +62,6 @@ abstract class VcsDriver implements VcsDriverInterface
return false; return false;
} }
/** /**
* Get the https or http protocol depending on SSL support. * Get the https or http protocol depending on SSL support.
* *

View File

@ -30,10 +30,11 @@ class VcsRepository extends ArrayRepository
protected $packageName; protected $packageName;
protected $verbose; protected $verbose;
protected $io; protected $io;
protected $config;
protected $versionParser; protected $versionParser;
protected $type; protected $type;
public function __construct(array $repoConfig, IOInterface $io, Config $config = null, array $drivers = null) public function __construct(array $repoConfig, IOInterface $io, Config $config, array $drivers = null)
{ {
$this->drivers = $drivers ?: array( $this->drivers = $drivers ?: array(
'github' => 'Composer\Repository\Vcs\GitHubDriver', 'github' => 'Composer\Repository\Vcs\GitHubDriver',
@ -48,20 +49,21 @@ class VcsRepository extends ArrayRepository
$this->io = $io; $this->io = $io;
$this->type = isset($repoConfig['type']) ? $repoConfig['type'] : 'vcs'; $this->type = isset($repoConfig['type']) ? $repoConfig['type'] : 'vcs';
$this->verbose = $io->isVerbose(); $this->verbose = $io->isVerbose();
$this->config = $config;
} }
public function getDriver() public function getDriver()
{ {
if (isset($this->drivers[$this->type])) { if (isset($this->drivers[$this->type])) {
$class = $this->drivers[$this->type]; $class = $this->drivers[$this->type];
$driver = new $class($this->url, $this->io); $driver = new $class($this->url, $this->io, $this->config);
$driver->initialize(); $driver->initialize();
return $driver; return $driver;
} }
foreach ($this->drivers as $driver) { foreach ($this->drivers as $driver) {
if ($driver::supports($this->io, $this->url)) { if ($driver::supports($this->io, $this->url)) {
$driver = new $driver($this->url, $this->io); $driver = new $driver($this->url, $this->io, $this->config);
$driver->initialize(); $driver->initialize();
return $driver; return $driver;
} }
@ -69,7 +71,7 @@ class VcsRepository extends ArrayRepository
foreach ($this->drivers as $driver) { foreach ($this->drivers as $driver) {
if ($driver::supports($this->io, $this->url, true)) { if ($driver::supports($this->io, $this->url, true)) {
$driver = new $driver($this->url, $this->io); $driver = new $driver($this->url, $this->io, $this->config);
$driver->initialize(); $driver->initialize();
return $driver; return $driver;
} }
@ -102,7 +104,7 @@ class VcsRepository extends ArrayRepository
} }
foreach ($driver->getTags() as $tag => $identifier) { foreach ($driver->getTags() as $tag => $identifier) {
$msg = 'Get composer info for <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $tag . '</comment>)'; $msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $tag . '</comment>)';
if ($verbose) { if ($verbose) {
$this->io->write($msg); $this->io->write($msg);
} else { } else {
@ -123,12 +125,6 @@ class VcsRepository extends ArrayRepository
} }
continue; continue;
} }
} catch (\Exception $e) {
if ($verbose) {
$this->io->write('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()));
}
continue;
}
// manually versioned package // manually versioned package
if (isset($data['version'])) { if (isset($data['version'])) {
@ -156,12 +152,18 @@ class VcsRepository extends ArrayRepository
} }
$this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier)));
} catch (\Exception $e) {
if ($verbose) {
$this->io->write('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()));
}
continue;
}
} }
$this->io->overwrite('', false); $this->io->overwrite('', false);
foreach ($driver->getBranches() as $branch => $identifier) { foreach ($driver->getBranches() as $branch => $identifier) {
$msg = 'Get composer info for <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)'; $msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)';
if ($verbose) { if ($verbose) {
$this->io->write($msg); $this->io->write($msg);
} else { } else {
@ -182,15 +184,6 @@ class VcsRepository extends ArrayRepository
} }
continue; continue;
} }
} catch (TransportException $e) {
if ($verbose) {
$this->io->write('Skipped branch '.$branch.', no composer file was found');
}
continue;
} catch (\Exception $e) {
$this->io->write('Skipped branch '.$branch.', '.$e->getMessage());
continue;
}
// branches are always auto-versionned, read value from branch name // branches are always auto-versionned, read value from branch name
$data['version'] = $branch; $data['version'] = $branch;
@ -208,6 +201,15 @@ class VcsRepository extends ArrayRepository
} }
$this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier)));
} catch (TransportException $e) {
if ($verbose) {
$this->io->write('Skipped branch '.$branch.', no composer file was found');
}
continue;
} catch (\Exception $e) {
$this->io->write('Skipped branch '.$branch.', '.$e->getMessage());
continue;
}
} }
$this->io->overwrite('', false); $this->io->overwrite('', false);

View File

@ -34,22 +34,50 @@ final class StreamContextFactory
// Handle system proxy // Handle system proxy
if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) { if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) {
// Some systems seem to rely on a lowercased version instead... // Some systems seem to rely on a lowercased version instead...
$proxy = isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']; $proxy = parse_url(isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
}
if (!empty($proxy)) {
$proxyURL = (isset($proxy['scheme']) ? $proxy['scheme'] : '') . '://';
$proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
if (isset($proxy['port'])) {
$proxyURL .= ":" . $proxy['port'];
} elseif ('http://' == substr($proxyURL, 0, 7)) {
$proxyURL .= ":80";
} elseif ('https://' == substr($proxyURL, 0, 8)) {
$proxyURL .= ":443";
}
// http(s):// is not supported in proxy // http(s):// is not supported in proxy
$proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxy); $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL);
if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) {
throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
} }
$options['http'] = array( $options['http'] = array(
'proxy' => $proxy, 'proxy' => $proxyURL,
'request_fulluri' => true, 'request_fulluri' => true,
); );
if (isset($proxy['user'])) {
$auth = $proxy['user'];
if (isset($proxy['pass'])) {
$auth .= ':' . $proxy['pass'];
}
$auth = base64_encode($auth);
// Preserve headers if already set in default options
if (isset($defaultOptions['http']['header'])) {
$defaultOptions['http']['header'] .= "Proxy-Authorization: Basic {$auth}\r\n";
} else {
$options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n";
}
}
} }
$options = array_merge_recursive($options, $defaultOptions); $options = array_replace_recursive($options, $defaultOptions);
return stream_context_create($options, $defaultParams); return stream_context_create($options, $defaultParams);
} }

View File

@ -16,7 +16,7 @@ function includeIfExists($file) {
} }
} }
if ((!$loader = includeIfExists(__DIR__.'/../vendor/.composer/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../.composer/autoload.php'))) { if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) {
die('You must set up the project dependencies, run the following commands:'.PHP_EOL. die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -s http://getcomposer.org/installer | php'.PHP_EOL. 'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL); 'php composer.phar install'.PHP_EOL);

View File

@ -60,14 +60,14 @@ class AutoloadGeneratorTest extends TestCase
protected function tearDown() protected function tearDown()
{ {
if ($this->vendorDir === $this->workingDir) { if ($this->vendorDir === $this->workingDir) {
if (is_dir($this->workingDir.'/.composer')) { if (is_dir($this->workingDir.'/composer')) {
$this->fs->removeDirectory($this->workingDir.'/.composer'); $this->fs->removeDirectory($this->workingDir.'/composer');
} }
} elseif (is_dir($this->vendorDir)) { } elseif (is_dir($this->vendorDir)) {
$this->fs->removeDirectory($this->vendorDir); $this->fs->removeDirectory($this->vendorDir);
} }
if (is_dir($this->workingDir.'/.composersrc')) { if (is_dir($this->workingDir.'/composersrc')) {
$this->fs->removeDirectory($this->workingDir.'/.composersrc'); $this->fs->removeDirectory($this->workingDir.'/composersrc');
} }
chdir($this->dir); chdir($this->dir);
@ -78,22 +78,22 @@ class AutoloadGeneratorTest extends TestCase
$package = new MemoryPackage('a', '1.0', '1.0'); $package = new MemoryPackage('a', '1.0', '1.0');
$package->setAutoload(array( $package->setAutoload(array(
'psr-0' => array('Main' => 'src/', 'Lala' => array('src/', 'lib/')), 'psr-0' => array('Main' => 'src/', 'Lala' => array('src/', 'lib/')),
'classmap' => array('.composersrc/'), 'classmap' => array('composersrc/'),
)); ));
$this->repository->expects($this->once()) $this->repository->expects($this->once())
->method('getPackages') ->method('getPackages')
->will($this->returnValue(array())); ->will($this->returnValue(array()));
if (!is_dir($this->vendorDir.'/.composer')) { if (!is_dir($this->vendorDir.'/composer')) {
mkdir($this->vendorDir.'/.composer'); mkdir($this->vendorDir.'/composer');
} }
$this->createClassFile($this->workingDir); $this->createClassFile($this->workingDir);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertAutoloadFiles('main', $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('main', $this->vendorDir.'/composer');
$this->assertAutoloadFiles('classmap', $this->vendorDir.'/.composer', 'classmap'); $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap');
} }
public function testVendorDirSameAsWorkingDir() public function testVendorDirSameAsWorkingDir()
@ -103,22 +103,22 @@ class AutoloadGeneratorTest extends TestCase
$package = new MemoryPackage('a', '1.0', '1.0'); $package = new MemoryPackage('a', '1.0', '1.0');
$package->setAutoload(array( $package->setAutoload(array(
'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
'classmap' => array('.composersrc/'), 'classmap' => array('composersrc/'),
)); ));
$this->repository->expects($this->once()) $this->repository->expects($this->once())
->method('getPackages') ->method('getPackages')
->will($this->returnValue(array())); ->will($this->returnValue(array()));
if (!is_dir($this->vendorDir.'/.composer')) { if (!is_dir($this->vendorDir.'/composer')) {
mkdir($this->vendorDir.'/.composer', 0777, true); mkdir($this->vendorDir.'/composer', 0777, true);
} }
$this->createClassFile($this->vendorDir); $this->createClassFile($this->vendorDir);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertAutoloadFiles('main3', $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('main3', $this->vendorDir.'/composer');
$this->assertAutoloadFiles('classmap3', $this->vendorDir.'/.composer', 'classmap'); $this->assertAutoloadFiles('classmap3', $this->vendorDir.'/composer', 'classmap');
} }
public function testMainPackageAutoloadingAlternativeVendorDir() public function testMainPackageAutoloadingAlternativeVendorDir()
@ -126,7 +126,7 @@ class AutoloadGeneratorTest extends TestCase
$package = new MemoryPackage('a', '1.0', '1.0'); $package = new MemoryPackage('a', '1.0', '1.0');
$package->setAutoload(array( $package->setAutoload(array(
'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'),
'classmap' => array('.composersrc/'), 'classmap' => array('composersrc/'),
)); ));
$this->repository->expects($this->once()) $this->repository->expects($this->once())
@ -134,11 +134,27 @@ class AutoloadGeneratorTest extends TestCase
->will($this->returnValue(array())); ->will($this->returnValue(array()));
$this->vendorDir .= '/subdir'; $this->vendorDir .= '/subdir';
mkdir($this->vendorDir.'/.composer', 0777, true); mkdir($this->vendorDir.'/composer', 0777, true);
$this->createClassFile($this->workingDir); $this->createClassFile($this->workingDir);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertAutoloadFiles('main2', $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('main2', $this->vendorDir.'/composer');
$this->assertAutoloadFiles('classmap2', $this->vendorDir.'/.composer', 'classmap'); $this->assertAutoloadFiles('classmap2', $this->vendorDir.'/composer', 'classmap');
}
public function testMainPackageAutoloadingWithTargetDir()
{
$package = new MemoryPackage('a', '1.0', '1.0');
$package->setAutoload(array(
'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''),
));
$package->setTargetDir('Main/Foo/');
$this->repository->expects($this->once())
->method('getPackages')
->will($this->returnValue(array()));
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertFileEquals(__DIR__.'/Fixtures/autoload_target_dir.php', $this->vendorDir.'/autoload.php');
} }
public function testVendorsAutoloading() public function testVendorsAutoloading()
@ -155,10 +171,10 @@ class AutoloadGeneratorTest extends TestCase
->method('getPackages') ->method('getPackages')
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
mkdir($this->vendorDir.'/.composer', 0777, true); mkdir($this->vendorDir.'/composer', 0777, true);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertAutoloadFiles('vendors', $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('vendors', $this->vendorDir.'/composer');
$this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty.");
} }
public function testVendorsClassMapAutoloading() public function testVendorsClassMapAutoloading()
@ -175,7 +191,7 @@ class AutoloadGeneratorTest extends TestCase
->method('getPackages') ->method('getPackages')
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
@mkdir($this->vendorDir.'/.composer', 0777, true); @mkdir($this->vendorDir.'/composer', 0777, true);
mkdir($this->vendorDir.'/a/a/src', 0777, true); mkdir($this->vendorDir.'/a/a/src', 0777, true);
mkdir($this->vendorDir.'/b/b/src', 0777, true); mkdir($this->vendorDir.'/b/b/src', 0777, true);
mkdir($this->vendorDir.'/b/b/lib', 0777, true); mkdir($this->vendorDir.'/b/b/lib', 0777, true);
@ -183,17 +199,17 @@ class AutoloadGeneratorTest extends TestCase
file_put_contents($this->vendorDir.'/b/b/src/b.php', '<?php class ClassMapBar {}'); file_put_contents($this->vendorDir.'/b/b/src/b.php', '<?php class ClassMapBar {}');
file_put_contents($this->vendorDir.'/b/b/lib/c.php', '<?php class ClassMapBaz {}'); file_put_contents($this->vendorDir.'/b/b/lib/c.php', '<?php class ClassMapBaz {}');
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated.");
$this->assertEquals( $this->assertEquals(
array( array(
'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php',
'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php',
'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php',
), ),
include ($this->vendorDir.'/.composer/autoload_classmap.php') include ($this->vendorDir.'/composer/autoload_classmap.php')
); );
$this->assertAutoloadFiles('classmap4', $this->vendorDir.'/.composer', 'classmap'); $this->assertAutoloadFiles('classmap4', $this->vendorDir.'/composer', 'classmap');
} }
public function testClassMapAutoloadingEmptyDirAndExactFile() public function testClassMapAutoloadingEmptyDirAndExactFile()
@ -212,7 +228,7 @@ class AutoloadGeneratorTest extends TestCase
->method('getPackages') ->method('getPackages')
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
@mkdir($this->vendorDir.'/.composer', 0777, true); @mkdir($this->vendorDir.'/composer', 0777, true);
mkdir($this->vendorDir.'/a/a/src', 0777, true); mkdir($this->vendorDir.'/a/a/src', 0777, true);
mkdir($this->vendorDir.'/b/b', 0777, true); mkdir($this->vendorDir.'/b/b', 0777, true);
mkdir($this->vendorDir.'/c/c/foo', 0777, true); mkdir($this->vendorDir.'/c/c/foo', 0777, true);
@ -220,17 +236,17 @@ class AutoloadGeneratorTest extends TestCase
file_put_contents($this->vendorDir.'/b/b/test.php', '<?php class ClassMapBar {}'); 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 {}'); file_put_contents($this->vendorDir.'/c/c/foo/test.php', '<?php class ClassMapBaz {}');
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated."); $this->assertTrue(file_exists($this->vendorDir.'/composer/autoload_classmap.php'), "ClassMap file needs to be generated.");
$this->assertEquals( $this->assertEquals(
array( array(
'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php',
'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/test.php', 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/test.php',
'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/c/c/foo/test.php', 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/c/c/foo/test.php',
), ),
include ($this->vendorDir.'/.composer/autoload_classmap.php') include ($this->vendorDir.'/composer/autoload_classmap.php')
); );
$this->assertAutoloadFiles('classmap5', $this->vendorDir.'/.composer', 'classmap'); $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap');
} }
public function testOverrideVendorsAutoloading() public function testOverrideVendorsAutoloading()
@ -248,9 +264,9 @@ class AutoloadGeneratorTest extends TestCase
->method('getPackages') ->method('getPackages')
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
mkdir($this->vendorDir.'/.composer', 0777, true); mkdir($this->vendorDir.'/composer', 0777, true);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/composer');
$this->assertAutoloadFiles('override_vendors', $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('override_vendors', $this->vendorDir.'/composer');
} }
public function testIncludePathFileGeneration() public function testIncludePathFileGeneration()
@ -264,23 +280,29 @@ class AutoloadGeneratorTest extends TestCase
$b = new MemoryPackage("b/b", "1.0", "1.0"); $b = new MemoryPackage("b/b", "1.0", "1.0");
$b->setIncludePaths(array("library")); $b->setIncludePaths(array("library"));
$c = new MemoryPackage("c", "1.0", "1.0");
$c->setIncludePaths(array("library"));
$packages[] = $a; $packages[] = $a;
$packages[] = $b; $packages[] = $b;
$packages[] = $c;
$this->repository->expects($this->once()) $this->repository->expects($this->once())
->method("getPackages") ->method("getPackages")
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
mkdir($this->vendorDir."/.composer", 0777, true); mkdir($this->vendorDir."/composer", 0777, true);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer");
$this->assertFileEquals(__DIR__.'/Fixtures/include_paths.php', $this->vendorDir.'/composer/include_paths.php');
$this->assertEquals( $this->assertEquals(
array( array(
$this->vendorDir."/a/a/lib/", $this->vendorDir."/a/a/lib",
$this->vendorDir."/b/b/library" $this->vendorDir."/b/b/library",
$this->vendorDir."/c/library",
), ),
require($this->vendorDir."/.composer/include_paths.php") require($this->vendorDir."/composer/include_paths.php")
); );
} }
@ -298,16 +320,16 @@ class AutoloadGeneratorTest extends TestCase
->method("getPackages") ->method("getPackages")
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
mkdir($this->vendorDir."/.composer", 0777, true); mkdir($this->vendorDir."/composer", 0777, true);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer");
$oldIncludePath = get_include_path(); $oldIncludePath = get_include_path();
require($this->vendorDir."/.composer/autoload.php"); require($this->vendorDir."/autoload.php");
$this->assertEquals( $this->assertEquals(
$oldIncludePath.PATH_SEPARATOR.$this->vendorDir."/a/a/lib/", $oldIncludePath.PATH_SEPARATOR.$this->vendorDir."/a/a/lib",
get_include_path() get_include_path()
); );
@ -326,20 +348,20 @@ class AutoloadGeneratorTest extends TestCase
->method("getPackages") ->method("getPackages")
->will($this->returnValue($packages)); ->will($this->returnValue($packages));
mkdir($this->vendorDir."/.composer", 0777, true); mkdir($this->vendorDir."/composer", 0777, true);
$this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/.composer"); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir."/composer");
$this->assertFalse(file_exists($this->vendorDir."/.composer/include_paths.php")); $this->assertFalse(file_exists($this->vendorDir."/composer/include_paths.php"));
} }
private function createClassFile($basedir) private function createClassFile($basedir)
{ {
if (!is_dir($basedir.'/.composersrc')) { if (!is_dir($basedir.'/composersrc')) {
mkdir($basedir.'/.composersrc', 0777, true); mkdir($basedir.'/composersrc', 0777, true);
} }
file_put_contents($basedir.'/.composersrc/foo.php', '<?php class ClassMapFoo {}'); file_put_contents($basedir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
} }
private function assertAutoloadFiles($name, $dir, $type = 'namespaces') private function assertAutoloadFiles($name, $dir, $type = 'namespaces')

View File

@ -6,5 +6,5 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'ClassMapFoo' => $baseDir . '/.composersrc/foo.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php',
); );

View File

@ -6,5 +6,5 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname(dirname($vendorDir)); $baseDir = dirname(dirname($vendorDir));
return array( return array(
'ClassMapFoo' => $baseDir . '/.composersrc/foo.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php',
); );

View File

@ -6,5 +6,5 @@ $vendorDir = dirname(__DIR__);
$baseDir = $vendorDir; $baseDir = $vendorDir;
return array( return array(
'ClassMapFoo' => $baseDir . '/.composersrc/foo.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php',
); );

View File

@ -0,0 +1,41 @@
<?php
// autoload.php generated by Composer
if (!class_exists('Composer\\Autoload\\ClassLoader', false)) {
require __DIR__ . '/composer' . '/ClassLoader.php';
}
return call_user_func(function() {
$loader = new \Composer\Autoload\ClassLoader();
$composerDir = __DIR__ . '/composer';
$map = require $composerDir . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->add($namespace, $path);
}
$classMap = require $composerDir . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
spl_autoload_register(function($class) {
$dir = dirname(__DIR__) . '/';
$prefixes = array('Main\\Foo', 'Main\\Bar');
foreach ($prefixes as $prefix) {
if (0 !== strpos($class, $prefix)) {
continue;
}
$path = $dir . implode('/', array_slice(explode('\\', $class), 2)).'.php';
if (!stream_resolve_include_path($path)) {
return false;
}
require_once $path;
return true;
}
});
$loader->register();
return $loader;
});

View File

@ -0,0 +1,12 @@
<?php
// include_paths.php generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
$vendorDir . '/a/a/lib',
$vendorDir . '/b/b/library',
$vendorDir . '/c/library',
);

View File

@ -18,6 +18,7 @@ use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Literal; use Composer\DependencyResolver\Literal;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Package\AliasPackage;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Test\TestCase; use Composer\Test\TestCase;
@ -99,6 +100,35 @@ class DefaultPolicyTest extends TestCase
$this->assertEquals($expected, $selected); $this->assertEquals($expected, $selected);
} }
public function testSelectLocalReposFirst()
{
$this->repoImportant = new ArrayRepository;
$this->repo->addPackage($packageA = $this->getPackage('A', 'dev-master'));
$this->repo->addPackage($packageAAlias = new AliasPackage($packageA, '2.1.9999999.9999999-dev', '2.1.x-dev'));
$this->repoImportant->addPackage($packageAImportant = $this->getPackage('A', 'dev-feature-a'));
$this->repoImportant->addPackage($packageAAliasImportant = new AliasPackage($packageAImportant, '2.1.9999999.9999999-dev', '2.1.x-dev'));
$this->repoImportant->addPackage($packageA2Important = $this->getPackage('A', 'dev-master'));
$this->repoImportant->addPackage($packageA2AliasImportant = new AliasPackage($packageA2Important, '2.1.9999999.9999999-dev', '2.1.x-dev'));
$packageAAliasImportant->setRootPackageAlias(true);
$this->pool->addRepository($this->repoInstalled);
$this->pool->addRepository($this->repoImportant);
$this->pool->addRepository($this->repo);
$packages = $this->pool->whatProvides('a', new VersionConstraint('=', '2.1.9999999.9999999-dev'));
$literals = array();
foreach ($packages as $package) {
$literals[] = new Literal($package, true);
}
$expected = array(new Literal($packageAAliasImportant, true));
$selected = $this->policy->selectPreferedPackages($this->pool, array(), $literals);
$this->assertEquals($expected, $selected);
}
public function testSelectAllProviders() public function testSelectAllProviders()
{ {
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));

View File

@ -16,10 +16,17 @@ use Composer\Downloader\DownloadManager;
class DownloadManagerTest extends \PHPUnit_Framework_TestCase class DownloadManagerTest extends \PHPUnit_Framework_TestCase
{ {
protected $filesystem;
public function setUp()
{
$this->filesystem = $this->getMock('Composer\Util\Filesystem');
}
public function testSetGetDownloader() public function testSetGetDownloader()
{ {
$downloader = $this->createDownloaderMock(); $downloader = $this->createDownloaderMock();
$manager = new DownloadManager(); $manager = new DownloadManager(false, $this->filesystem);
$manager->setDownloader('test', $downloader); $manager->setDownloader('test', $downloader);
$this->assertSame($downloader, $manager->getDownloader('test')); $this->assertSame($downloader, $manager->getDownloader('test'));
@ -36,7 +43,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getInstallationSource') ->method('getInstallationSource')
->will($this->returnValue(null)); ->will($this->returnValue(null));
$manager = new DownloadManager(); $manager = new DownloadManager(false, $this->filesystem);
$this->setExpectedException('InvalidArgumentException'); $this->setExpectedException('InvalidArgumentException');
@ -62,6 +69,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue('dist')); ->will($this->returnValue('dist'));
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloader')) ->setMethods(array('getDownloader'))
->getMock(); ->getMock();
@ -93,6 +101,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue('source')); ->will($this->returnValue('source'));
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloader')) ->setMethods(array('getDownloader'))
->getMock(); ->getMock();
@ -126,6 +135,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue('source')); ->will($this->returnValue('source'));
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloader')) ->setMethods(array('getDownloader'))
->getMock(); ->getMock();
@ -157,6 +167,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue('dist')); ->will($this->returnValue('dist'));
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloader')) ->setMethods(array('getDownloader'))
->getMock(); ->getMock();
@ -195,6 +206,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -218,7 +230,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType') ->method('getDistType')
->will($this->returnValue(null)); ->will($this->returnValue(null));
$manager = new DownloadManager(); $manager = new DownloadManager(false, $this->filesystem);
$this->setExpectedException('InvalidArgumentException'); $this->setExpectedException('InvalidArgumentException');
$manager->download($package, 'target_dir'); $manager->download($package, 'target_dir');
@ -248,6 +260,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -283,6 +296,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -318,6 +332,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -354,6 +369,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -390,6 +406,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'target_dir'); ->with($package, 'target_dir');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -414,7 +431,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType') ->method('getDistType')
->will($this->returnValue(null)); ->will($this->returnValue(null));
$manager = new DownloadManager(); $manager = new DownloadManager(false, $this->filesystem);
$manager->setPreferSource(true); $manager->setPreferSource(true);
$this->setExpectedException('InvalidArgumentException'); $this->setExpectedException('InvalidArgumentException');
@ -450,6 +467,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($initial, $target, 'vendor/bundles/FOS/UserBundle'); ->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager
@ -486,6 +504,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($initial, 'vendor/bundles/FOS/UserBundle'); ->with($initial, 'vendor/bundles/FOS/UserBundle');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
->getMock(); ->getMock();
$manager $manager
@ -526,6 +545,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($initial, $target, 'vendor/pkg'); ->with($initial, $target, 'vendor/pkg');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
->getMock(); ->getMock();
$manager $manager
@ -562,6 +582,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($initial, 'vendor/pkg'); ->with($initial, 'vendor/pkg');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage', 'download')) ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
->getMock(); ->getMock();
$manager $manager
@ -588,6 +609,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->with($package, 'vendor/bundles/FOS/UserBundle'); ->with($package, 'vendor/bundles/FOS/UserBundle');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array(false, $this->filesystem))
->setMethods(array('getDownloaderForInstalledPackage')) ->setMethods(array('getDownloaderForInstalledPackage'))
->getMock(); ->getMock();
$manager $manager

View File

@ -140,7 +140,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
public function testUpdate() public function testUpdate()
{ {
$expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'"); $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'");
$expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no");
$packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any()) $packageMock->expects($this->any())
@ -173,7 +173,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
public function testUpdateThrowsRuntimeExceptionIfGitCommandFails() public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
{ {
$expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'"); $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer && git checkout 'ref' && git reset --hard 'ref'");
$expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no");
$packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any()) $packageMock->expects($this->any())
@ -202,7 +202,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
public function testRemove() public function testRemove()
{ {
$expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain --untracked-files=no");
$packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock = $this->getMock('Composer\Package\PackageInterface');
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor');

View File

@ -0,0 +1,115 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test;
use Composer\Installer;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositoryManager;
use Composer\Repository\RepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Package\Link;
use Composer\Test\Mock\WritableRepositoryMock;
use Composer\Test\Mock\InstallationManagerMock;
class InstallerTest extends TestCase
{
/**
* @dataProvider provideInstaller
*/
public function testInstaller(PackageInterface $rootPackage, $repositories, array $options)
{
$io = $this->getMock('Composer\IO\IOInterface');
$downloadManager = $this->getMock('Composer\Downloader\DownloadManager');
$config = $this->getMock('Composer\Config');
$repositoryManager = new RepositoryManager($io, $config);
$repositoryManager->setLocalRepository(new WritableRepositoryMock());
$repositoryManager->setLocalDevRepository(new WritableRepositoryMock());
if (!is_array($repositories)) {
$repositories = array($repositories);
}
foreach ($repositories as $repository) {
$repositoryManager->addRepository($repository);
}
$locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock();
$installationManager = new InstallationManagerMock();
$eventDispatcher = $this->getMockBuilder('Composer\Script\EventDispatcher')->disableOriginalConstructor()->getMock();
$autoloadGenerator = $this->getMock('Composer\Autoload\AutoloadGenerator');
$installer = new Installer($io, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator);
$result = $installer->run();
$this->assertTrue($result);
$expectedInstalled = isset($options['install']) ? $options['install'] : array();
$expectedUpdated = isset($options['update']) ? $options['update'] : array();
$expectedUninstalled = isset($options['uninstall']) ? $options['uninstall'] : array();
$installed = $installationManager->getInstalledPackages();
$this->assertSame($expectedInstalled, $installed);
$updated = $installationManager->getUpdatedPackages();
$this->assertSame($expectedUpdated, $updated);
$uninstalled = $installationManager->getUninstalledPackages();
$this->assertSame($expectedUninstalled, $uninstalled);
}
public function provideInstaller()
{
$cases = array();
// when A requires B and B requires A, and A is a non-published root package
// the install of B should succeed
$a = $this->getPackage('A', '1.0.0');
$a->setRequires(array(
new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')),
));
$b = $this->getPackage('B', '1.0.0');
$b->setRequires(array(
new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')),
));
$cases[] = array(
$a,
new ArrayRepository(array($b)),
array(
'install' => array($b)
),
);
// #480: when A requires B and B requires A, and A is a published root package
// only B should be installed, as A is the root
$a = $this->getPackage('A', '1.0.0');
$a->setRequires(array(
new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')),
));
$b = $this->getPackage('B', '1.0.0');
$b->setRequires(array(
new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')),
));
$cases[] = array(
$a,
new ArrayRepository(array($a, $b)),
array(
'install' => array($b)
),
);
return $cases;
}
}

View File

@ -128,6 +128,20 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
$this->assertJsonFormat($json, $data); $this->assertJsonFormat($json, $data);
} }
public function testFormatEmptyArray()
{
$data = array('test' => array(), 'test2' => new \stdClass);
$json = '{
"test": [
],
"test2": {
}
}';
$this->assertJsonFormat($json, $data);
}
public function testEscape() public function testEscape()
{ {
$data = array("Metadata\\\"" => 'src/'); $data = array("Metadata\\\"" => 'src/');

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Mock;
use Composer\Installer\InstallationManager;
use Composer\Repository\RepositoryInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
class InstallationManagerMock extends InstallationManager
{
private $installed = array();
private $updated = array();
private $uninstalled = array();
public function install(RepositoryInterface $repo, InstallOperation $operation)
{
$this->installed[] = $operation->getPackage();
}
public function update(RepositoryInterface $repo, UpdateOperation $operation)
{
$this->updated[] = array($operation->getInitialPackage(), $operation->getTargetPackage());
}
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
{
$this->uninstalled[] = $operation->getPackage();
}
public function getInstalledPackages()
{
return $this->installed;
}
public function getUpdatedPackages()
{
return $this->updated;
}
public function getUninstalledPackages()
{
return $this->uninstalled;
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Mock;
use Composer\Repository\ArrayRepository;
use Composer\Repository\WritableRepositoryInterface;
class WritableRepositoryMock extends ArrayRepository implements WritableRepositoryInterface
{
public function reload()
{
}
public function write()
{
}
}

View File

@ -89,7 +89,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase
'homepage' => 'http://example.com', 'homepage' => 'http://example.com',
'license' => array('MIT', 'GPLv3'), 'license' => array('MIT', 'GPLv3'),
'authors' => array( 'authors' => array(
array('name' => 'Bob', 'email' => 'bob@example.org', 'homepage' => 'example.org'), array('name' => 'Bob', 'email' => 'bob@example.org', 'homepage' => 'example.org', 'role' => 'Developer'),
), ),
'require' => array( 'require' => array(
'foo/bar' => '1.0', 'foo/bar' => '1.0',

View File

@ -15,6 +15,7 @@ namespace Composer\Test\Repository\Vcs;
use Composer\Downloader\TransportException; use Composer\Downloader\TransportException;
use Composer\Repository\Vcs\GitHubDriver; use Composer\Repository\Vcs\GitHubDriver;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Config;
/** /**
* @author Beau Simensen <beau@dflydev.com> * @author Beau Simensen <beau@dflydev.com>
@ -64,7 +65,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
->will($this->returnValue('{"master_branch": "test_master"}')); ->will($this->returnValue('{"master_branch": "test_master"}'));
$gitHubDriver = new GitHubDriver($repoUrl, $io, null, $remoteFilesystem); $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem);
$gitHubDriver->initialize(); $gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@ -114,7 +115,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
->will($this->returnValue('{"master_branch": "test_master"}')); ->will($this->returnValue('{"master_branch": "test_master"}'));
$gitHubDriver = new GitHubDriver($repoUrl, $io, null, $remoteFilesystem); $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem);
$gitHubDriver->initialize(); $gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@ -171,7 +172,14 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
// clean local clone if present // clean local clone if present
$fs = new Filesystem(); $fs = new Filesystem();
$fs->removeDirectory(sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9.]}i', '-', $repoSshUrl) . '/'); $fs->removeDirectory(sys_get_temp_dir() . '/composer-test');
$config = new Config();
$config->merge(array(
'config' => array(
'home' => sys_get_temp_dir() . '/composer-test',
),
));
$process->expects($this->at(0)) $process->expects($this->at(0))
->method('execute') ->method('execute')
@ -202,7 +210,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
->method('splitLines') ->method('splitLines')
->will($this->returnValue(array('* test_master'))); ->will($this->returnValue(array('* test_master')));
$gitHubDriver = new GitHubDriver($repoUrl, $io, $process, $remoteFilesystem); $gitHubDriver = new GitHubDriver($repoUrl, $io, $config, $process, $remoteFilesystem);
$gitHubDriver->initialize(); $gitHubDriver->initialize();
$this->assertEquals('test_master', $gitHubDriver->getRootIdentifier()); $this->assertEquals('test_master', $gitHubDriver->getRootIdentifier());

View File

@ -14,6 +14,7 @@ namespace Composer\Test\Repository\Vcs;
use Composer\Repository\Vcs\SvnDriver; use Composer\Repository\Vcs\SvnDriver;
use Composer\IO\NullIO; use Composer\IO\NullIO;
use Composer\Config;
class SvnDriverTest extends \PHPUnit_Framework_TestCase class SvnDriverTest extends \PHPUnit_Framework_TestCase
{ {
@ -39,8 +40,8 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase
->method('getErrorOutput') ->method('getErrorOutput')
->will($this->returnValue($output)); ->will($this->returnValue($output));
$svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, $process); $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, new Config(), $process);
$svn->getTags(); $svn->initialize();
} }
private function getCmd($cmd) private function getCmd($cmd)

View File

@ -19,6 +19,7 @@ use Composer\Repository\Vcs\GitDriver;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Util\ProcessExecutor; use Composer\Util\ProcessExecutor;
use Composer\IO\NullIO; use Composer\IO\NullIO;
use Composer\Config;
class VcsRepositoryTest extends \PHPUnit_Framework_TestCase class VcsRepositoryTest extends \PHPUnit_Framework_TestCase
{ {
@ -123,7 +124,7 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase
'dev-master' => true, 'dev-master' => true,
); );
$repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO); $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO, new Config());
$packages = $repo->getPackages(); $packages = $repo->getPackages();
$dumper = new ArrayDumper(); $dumper = new ArrayDumper();

View File

@ -19,26 +19,29 @@ use Composer\Util\Filesystem;
abstract class TestCase extends \PHPUnit_Framework_TestCase abstract class TestCase extends \PHPUnit_Framework_TestCase
{ {
private static $versionParser; private static $parser;
public static function setUpBeforeClass() protected static function getVersionParser()
{ {
if (!self::$versionParser) { if (!self::$parser) {
self::$versionParser = new VersionParser(); self::$parser = new VersionParser();
} }
return self::$parser;
} }
protected function getVersionConstraint($operator, $version) protected function getVersionConstraint($operator, $version)
{ {
return new VersionConstraint( return new VersionConstraint(
$operator, $operator,
self::$versionParser->normalize($version) self::getVersionParser()->normalize($version)
); );
} }
protected function getPackage($name, $version) protected function getPackage($name, $version)
{ {
$normVersion = self::$versionParser->normalize($version); $normVersion = self::getVersionParser()->normalize($version);
return new MemoryPackage($name, $normVersion, $version); return new MemoryPackage($name, $normVersion, $version);
} }

View File

@ -57,31 +57,63 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
public function testHttpProxy() public function testHttpProxy()
{ {
$_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:port/'; $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:3128/';
$_SERVER['HTTP_PROXY'] = 'http://proxyserver/'; $_SERVER['HTTP_PROXY'] = 'http://proxyserver/';
$context = StreamContextFactory::getContext(array('http' => array('method' => 'GET'))); $context = StreamContextFactory::getContext(array('http' => array('method' => 'GET')));
$options = stream_context_get_options($context); $options = stream_context_get_options($context);
$this->assertSame('http://proxyserver/', $_SERVER['HTTP_PROXY']);
$this->assertEquals(array('http' => array( $this->assertEquals(array('http' => array(
'proxy' => 'tcp://username:password@proxyserver.net:port/', 'proxy' => 'tcp://proxyserver.net:3128',
'request_fulluri' => true, 'request_fulluri' => true,
'method' => 'GET', 'method' => 'GET',
'header' => "Proxy-Authorization: Basic " . base64_encode('username:password') . "\r\n"
)), $options); )), $options);
} }
public function testSSLProxy() public function testOptionsArePreserved()
{ {
$_SERVER['http_proxy'] = 'https://proxyserver/'; $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:3128/';
$context = StreamContextFactory::getContext(array('http' => array('method' => 'GET', 'header' => "X-Foo: bar\r\n", 'request_fulluri' => false)));
$options = stream_context_get_options($context);
$this->assertEquals(array('http' => array(
'proxy' => 'tcp://proxyserver.net:3128',
'request_fulluri' => false,
'method' => 'GET',
'header' => "X-Foo: bar\r\nProxy-Authorization: Basic " . base64_encode('username:password') . "\r\n"
)), $options);
}
public function testHttpProxyWithoutPort()
{
$_SERVER['http_proxy'] = 'http://username:password@proxyserver.net';
$context = StreamContextFactory::getContext(array('http' => array('method' => 'GET')));
$options = stream_context_get_options($context);
$this->assertEquals(array('http' => array(
'proxy' => 'tcp://proxyserver.net:80',
'request_fulluri' => true,
'method' => 'GET',
'header' => "Proxy-Authorization: Basic " . base64_encode('username:password') . "\r\n"
)), $options);
}
/**
* @dataProvider dataSSLProxy
*/
public function testSSLProxy($expected, $proxy)
{
$_SERVER['http_proxy'] = $proxy;
if (extension_loaded('openssl')) { if (extension_loaded('openssl')) {
$context = StreamContextFactory::getContext(); $context = StreamContextFactory::getContext();
$options = stream_context_get_options($context); $options = stream_context_get_options($context);
$this->assertSame(array('http' => array( $this->assertEquals(array('http' => array(
'proxy' => 'ssl://proxyserver/', 'proxy' => $expected,
'request_fulluri' => true, 'request_fulluri' => true,
)), $options); )), $options);
} else { } else {
@ -93,4 +125,12 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
} }
} }
} }
public function dataSSLProxy()
{
return array(
array('ssl://proxyserver:443', 'https://proxyserver/'),
array('ssl://proxyserver:8443', 'https://proxyserver:8443'),
);
}
} }