Merge remote-tracking branch 'upstream/master' into svn-auth-reloaded

main
till 12 years ago
commit c6566825ad

@ -2,13 +2,14 @@
* Added `create-project` command to install a project from scratch with composer
* Added automated `classmap` autoloading support for non-PSR-0 compliant projects
* Improved clones from GitHub which now automatically select between git/https/http protocols
* Added human readable error reporting when deps can not be solved
* Added support for private GitHub repositories (use --no-interaction for CI)
* Improved `validate` command to give more feedback
* Added "file" downloader type to download plain files
* Added support for authentication with svn repositories
* Removed dependency on filter_var
* Improved clones from GitHub which now automatically select between git/https/http protocols
* Improved `validate` command to give more feedback
* Improved the `search` & `show` commands output
* Removed dependency on filter_var
* Various robustness & error handling improvements, docs fixes and more
* 1.0.0-alpha1 (2012-03-01)

10
composer.lock generated

@ -14,18 +14,20 @@
{
"package": "symfony/console",
"version": "dev-master",
"source-reference": "2ee50c7c845ef7f8bce9c540709ecfd64cbcda87"
"source-reference": "88a283f6c18d1e644ebca1e3df4c870eac8108dd"
},
{
"package": "symfony/finder",
"version": "dev-master",
"source-reference": "b3adc8d5c29593db93c0abc4711a1e25fd3a6fa0"
"source-reference": "be30ecc95281d729ee51b9e89644d442bcf60451"
},
{
"package": "symfony/process",
"version": "dev-master",
"source-reference": "6aceac404d8574cf7da57e7e29b00a665b7bd559"
"source-reference": "0aad81ae9f884cf7df6387cb52a11b5b4f07b3d6"
}
],
"aliases": []
"aliases": [
]
}

@ -44,7 +44,7 @@ any version beginning with `1.0`.
## Installation
### 1) Downloading the Composer Executable
### Downloading the Composer Executable
To actually get Composer, we need to do two things. The first one is installing
Composer (again, this mean downloading it into your project):
@ -65,7 +65,7 @@ You can place this file anywhere you wish. If you put it in your `PATH`,
you can access it globally. On unixy systems you can even make it
executable and invoke it without `php`.
### 2) Using Composer
### Using Composer
Next, run the command the `install` command to resolve and download dependencies:

@ -144,7 +144,7 @@ means that we can just start using classes from it, and they will be
autoloaded.
$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Logger::WARNING));
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->addWarning('Foo');

@ -179,10 +179,15 @@ directory to something other than `vendor/bin`.
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
### http_proxy or HTTP_PROXY
If you are using composer from behind an HTTP proxy, you can use the standard
`HTTP_PROXY` or `http_proxy` env vars. Simply set it to the URL of your proxy.
`http_proxy` or `HTTP_PROXY` env vars. Simply set it to the URL of your proxy.
Many operating systems already set this variable for you.
Using `http_proxy` (lowercased) or even defining both might be preferrable since
some tools like git or curl will only use the lower-cased `http_proxy` version.
Alternatively you can also define the git proxy using
`git config --global http.proxy <proxy url>`.
&larr; [Libraries](02-libraries.md) | [Schema](04-schema.md) &rarr;

@ -181,8 +181,8 @@ class ClassLoader
$classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
foreach ($this->prefixes as $prefix => $dirs) {
foreach ($dirs as $dir) {
if (0 === strpos($class, $prefix)) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
return $dir . DIRECTORY_SEPARATOR . $classPath;
}

@ -29,4 +29,12 @@ abstract class Command extends BaseCommand
{
return $this->getApplication()->getComposer($required);
}
/**
* @return \Composer\IO\ConsoleIO
*/
protected function getIO()
{
return $this->getApplication()->getIO();
}
}

@ -23,6 +23,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Json\JsonFile;
use Composer\Util\RemoteFilesystem;
/**
* Install a package as new project into new directory.
@ -84,11 +85,11 @@ EOT
}
if (null === $repositoryUrl) {
$sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org'));
$sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org'), $this->getIO());
} elseif (".json" === substr($repositoryUrl, -5)) {
$sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl));
$sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io)));
} elseif (0 === strpos($repositoryUrl, 'http')) {
$sourceRepo = new ComposerRepository(array('url' => $repositoryUrl));
$sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $this->getIO());
} else {
throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url.");
}

@ -229,7 +229,7 @@ EOT
if (!$this->repos) {
$this->repos = new CompositeRepository(array(
new PlatformRepository,
new ComposerRepository(array('url' => 'http://packagist.org'))
new ComposerRepository(array('url' => 'http://packagist.org'), $this->getIO())
));
}

@ -61,6 +61,6 @@ EOT
->setInstallSuggests($input->getOption('install-suggests'))
;
return $install->run();
return $install->run() ? 0 : 1;
}
}

@ -54,7 +54,8 @@ EOT
} else {
$output->writeln('No composer.json found in the current directory, showing packages from packagist.org');
$installedRepo = $platformRepo;
$repos = new CompositeRepository(array($installedRepo, new ComposerRepository(array('url' => 'http://packagist.org'))));
$packagist = new ComposerRepository(array('url' => 'http://packagist.org'), $this->getIO());
$repos = new CompositeRepository(array($installedRepo, $packagist));
}
$tokens = $input->getArgument('tokens');

@ -13,7 +13,7 @@
namespace Composer\Command;
use Composer\Composer;
use Composer\Util\StreamContextFactory;
use Composer\Util\RemoteFilesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -40,9 +40,8 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$ctx = StreamContextFactory::getContext();
$latest = trim(file_get_contents('http://getcomposer.org/version', false, $ctx));
$rfs = new RemoteFilesystem($this->getIO());
$latest = trim($rfs->getContents('getcomposer.org', 'http://getcomposer.org/version', false));
if (Composer::VERSION !== $latest) {
$output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));
@ -50,7 +49,7 @@ EOT
$remoteFilename = 'http://getcomposer.org/composer.phar';
$localFilename = $_SERVER['argv'][0];
copy($remoteFilename, $localFilename, $ctx);
$rfs->copy('getcomposer.org', $remoteFilename, $localFilename);
} else {
$output->writeln("<info>You are using the latest composer version.</info>");
}

@ -65,7 +65,8 @@ EOT
} else {
$output->writeln('No composer.json found in the current directory, showing packages from packagist.org');
$installedRepo = $platformRepo;
$repos = new CompositeRepository(array($installedRepo, new ComposerRepository(array('url' => 'http://packagist.org'))));
$packagist = new ComposerRepository(array('url' => 'http://packagist.org'), $this->getIO());
$repos = new CompositeRepository(array($installedRepo, $packagist));
}
// show single package or single version

@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Json\JsonFile;
use Composer\Json\JsonValidationException;
use Composer\Util\RemoteFilesystem;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
@ -55,7 +56,7 @@ EOT
$laxValid = false;
try {
$json = new JsonFile($file);
$json = new JsonFile($file, new RemoteFilesystem($this->getIO()));
$json->read();
$json->validateSchema(JsonFile::LAX_SCHEMA);

@ -25,6 +25,7 @@ use Composer\Composer;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\IO\ConsoleIO;
use Composer\Util\ErrorHandler;
/**
* The console application that handles the commands
@ -40,6 +41,7 @@ class Application extends BaseApplication
public function __construct()
{
ErrorHandler::register();
parent::__construct('Composer', Composer::VERSION);
}

@ -0,0 +1,150 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\DependencyResolver;
/**
* Represents a problem detected while solving dependencies
*
* @author Nils Adermann <naderman@naderman.de>
*/
class Problem
{
/**
* A set of reasons for the problem, each is a rule or a job and a rule
* @var array
*/
protected $reasons;
/**
* Add a job as a reason
*
* @param array $job A job descriptor which is a reason for this problem
* @param Rule $rule An optional rule associated with the job
*/
public function addJobRule($job, Rule $rule = null)
{
$this->addReason(serialize($job), array(
'rule' => $rule,
'job' => $job,
));
}
/**
* Add a rule as a reason
*
* @param Rule $rule A rule which is a reason for this problem
*/
public function addRule(Rule $rule)
{
$this->addReason($rule->getId(), array(
'rule' => $rule,
'job' => null,
));
}
/**
* Retrieve all reasons for this problem
*
* @return array The problem's reasons
*/
public function getReasons()
{
return $this->reasons;
}
/**
* A human readable textual representation of the problem's reasons
*/
public function __toString()
{
if (count($this->reasons) === 1) {
reset($this->reasons);
$reason = current($this->reasons);
$rule = $reason['rule'];
$job = $reason['job'];
if ($job && $job['cmd'] === 'install' && empty($job['packages'])) {
// handle php extensions
if (0 === stripos($job['packageName'], 'ext-')) {
$ext = substr($job['packageName'], 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.phpversion($ext).') installed' : 'is missing from your system';
return 'The requested PHP extension "'.$job['packageName'].'" '.$this->constraintToText($job['constraint']).$error.'.';
}
return 'The requested package "'.$job['packageName'].'" '.$this->constraintToText($job['constraint']).'could not be found.';
}
}
$messages = array("Problem caused by:");
foreach ($this->reasons as $reason) {
$rule = $reason['rule'];
$job = $reason['job'];
if ($job) {
$messages[] = $this->jobToText($job);
} elseif ($rule) {
if ($rule instanceof Rule) {
$messages[] = $rule->toHumanReadableString();
}
}
}
return implode("\n\t\t\t- ", $messages);
}
/**
* Store a reason descriptor but ignore duplicates
*
* @param string $id A canonical identifier for the reason
* @param string $reason The reason descriptor
*/
protected function addReason($id, $reason)
{
if (!isset($this->reasons[$id])) {
$this->reasons[$id] = $reason;
}
}
/**
* Turns a job into a human readable description
*
* @param array $job
* @return string
*/
protected function jobToText($job)
{
switch ($job['cmd']) {
case 'install':
return 'Installation of package "'.$job['packageName'].'" '.$this->constraintToText($job['constraint']).'was requested. Satisfiable by packages ['.implode(', ', $job['packages']).'].';
case 'update':
return 'Update of package "'.$job['packageName'].'" '.$this->constraintToText($job['constraint']).'was requested.';
case 'remove':
return 'Removal of package "'.$job['packageName'].'" '.$this->constraintToText($job['constraint']).'was requested.';
}
return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.implode(', ', $job['packages']).'])';
}
/**
* Turns a constraint into text usable in a sentence describing a job
*
* @param LinkConstraint $constraint
* @return string
*/
protected function constraintToText($constraint)
{
return ($constraint) ? 'with constraint '.$constraint.' ' : '';
}
}

@ -52,6 +52,7 @@ class Request
'packages' => $packages,
'cmd' => $cmd,
'packageName' => $packageName,
'constraint' => $constraint,
);
}

@ -17,6 +17,19 @@ namespace Composer\DependencyResolver;
*/
class Rule
{
const RULE_INTERNAL_ALLOW_UPDATE = 1;
const RULE_JOB_INSTALL = 2;
const RULE_JOB_REMOVE = 3;
const RULE_JOB_LOCK = 4;
const RULE_NOT_INSTALLABLE = 5;
const RULE_PACKAGE_CONFLICT = 6;
const RULE_PACKAGE_REQUIRES = 7;
const RULE_PACKAGE_OBSOLETES = 8;
const RULE_INSTALLED_PACKAGE_OBSOLETES = 9;
const RULE_PACKAGE_SAME_NAME = 10;
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
const RULE_LEARNED = 12;
protected $disabled;
protected $literals;
protected $type;
@ -163,6 +176,68 @@ class Rule
}
}
public function toHumanReadableString()
{
$ruleText = '';
foreach ($this->literals as $i => $literal) {
if ($i != 0) {
$ruleText .= '|';
}
$ruleText .= $literal;
}
switch ($this->reason) {
case self::RULE_INTERNAL_ALLOW_UPDATE:
return $ruleText;
case self::RULE_JOB_INSTALL:
return "Install command rule ($ruleText)";
case self::RULE_JOB_REMOVE:
return "Remove command rule ($ruleText)";
case self::RULE_JOB_LOCK:
return "Lock command rule ($ruleText)";
case self::RULE_NOT_INSTALLABLE:
return $ruleText;
case self::RULE_PACKAGE_CONFLICT:
$package1 = $this->literals[0]->getPackage();
$package2 = $this->literals[1]->getPackage();
return 'Package "'.$package1.'" conflicts with "'.$package2.'"';
case self::RULE_PACKAGE_REQUIRES:
$literals = $this->literals;
$sourceLiteral = array_shift($literals);
$sourcePackage = $sourceLiteral->getPackage();
$requires = array();
foreach ($literals as $literal) {
$requires[] = $literal->getPackage();
}
$text = 'Package "'.$sourcePackage.'" contains the rule '.$this->reasonData.'. ';
if ($requires) {
$text .= 'Any of these packages satisfy the dependency: '.implode(', ', $requires).'.';
} else {
$text .= 'No package satisfies this dependency.';
}
return $text;
case self::RULE_PACKAGE_OBSOLETES:
return $ruleText;
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
return $ruleText;
case self::RULE_PACKAGE_SAME_NAME:
return $ruleText;
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
return $ruleText;
case self::RULE_LEARNED:
return 'learned: '.$ruleText;
}
}
/**
* Formats a rule as a string of the format (Literal1|Literal2|...)
*

@ -21,21 +21,6 @@ use Composer\DependencyResolver\Operation;
*/
class Solver
{
const RULE_INTERNAL_ALLOW_UPDATE = 1;
const RULE_JOB_INSTALL = 2;
const RULE_JOB_REMOVE = 3;
const RULE_JOB_LOCK = 4;
const RULE_NOT_INSTALLABLE = 5;
const RULE_NOTHING_PROVIDES_DEP = 6;
const RULE_PACKAGE_CONFLICT = 7;
const RULE_PACKAGE_NOT_EXIST = 8;
const RULE_PACKAGE_REQUIRES = 9;
const RULE_PACKAGE_OBSOLETES = 10;
const RULE_INSTALLED_PACKAGE_OBSOLETES = 11;
const RULE_PACKAGE_SAME_NAME = 12;
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 13;
const RULE_LEARNED = 14;
protected $policy;
protected $pool;
protected $installed;
@ -235,7 +220,7 @@ class Solver
}
if (!$dontFix && !$this->policy->installable($this, $this->pool, $this->installedMap, $package)) {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRemoveRule($package, self::RULE_NOT_INSTALLABLE, (string) $package));
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRemoveRule($package, Rule::RULE_NOT_INSTALLABLE, (string) $package));
continue;
}
@ -261,7 +246,7 @@ class Solver
}
}
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, self::RULE_PACKAGE_REQUIRES, (string) $link));
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, (string) $link));
foreach ($possibleRequires as $require) {
$workQueue->enqueue($require);
@ -276,7 +261,7 @@ class Solver
continue;
}
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, self::RULE_PACKAGE_CONFLICT, (string) $link));
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, (string) $link));
}
}
@ -301,7 +286,7 @@ class Solver
continue; // don't repair installed/installed problems
}
$reason = ($isInstalled) ? self::RULE_INSTALLED_PACKAGE_OBSOLETES : self::RULE_PACKAGE_OBSOLETES;
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
}
}
@ -327,7 +312,7 @@ class Solver
continue;
}
$reason = ($package->getName() == $provider->getName()) ? self::RULE_PACKAGE_SAME_NAME : self::RULE_PACKAGE_IMPLICIT_OBSOLETES;
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
}
}
@ -466,24 +451,29 @@ class Solver
$conflict = $this->findDecisionRule($literal->getPackage());
/** TODO: handle conflict with systemsolvable? */
$this->learnedPool[] = array($rule, $conflict);
if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
$problem = new Problem;
if ($rule->getType() == RuleSet::TYPE_JOB) {
$why = $this->ruleToJob[$rule->getId()];
$job = $this->ruleToJob[$rule->getId()];
$problem->addJobRule($job, $rule);
$problem->addRule($conflict);
$this->disableProblem($job);
} else {
$why = $rule;
$problem->addRule($rule);
$problem->addRule($conflict);
$this->disableProblem($rule);
}
$this->problems[] = array($why);
$this->disableProblem($why);
$this->problems[] = $problem;
continue;
}
// conflict with another job or update/feature rule
$this->problems[] = array();
$problem = new Problem;
$problem->addRule($rule);
$problem->addRule($conflict);
// push all of our rules (can only be feature or job rules)
// asserting this literal on the problem stack
@ -500,14 +490,16 @@ class Solver
}
if ($assertRule->getType() === RuleSet::TYPE_JOB) {
$why = $this->ruleToJob[$assertRule->getId()];
$job = $this->ruleToJob[$assertRule->getId()];
$problem->addJobRule($job, $assertRule);
$this->disableProblem($job);
} else {
$why = $assertRule;
$problem->addRule($assertRule);
$this->disableProblem($assertRule);
}
$this->problems[count($this->problems) - 1][] = $why;
$this->disableProblem($why);
}
$this->problems[] = $problem;
// start over
while (count($this->decisionQueue) > $decisionStart) {
@ -966,7 +958,7 @@ class Solver
foreach ($installedPackages as $package) {
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
$rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
$rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
$rule->setWeak(true);
$this->addRule(RuleSet::TYPE_FEATURE, $rule);
@ -977,9 +969,11 @@ class Solver
switch ($job['cmd']) {
case 'install':
if (empty($job['packages'])) {
$this->problems[] = array($job);
$problem = new Problem();
$problem->addJobRule($job);
$this->problems[] = $problem;
} else {
$rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
$rule = $this->createInstallOneOfRule($job['packages'], Rule::RULE_JOB_INSTALL, $job['packageName']);
$this->addRule(RuleSet::TYPE_JOB, $rule);
$this->ruleToJob[$rule->getId()] = $job;
}
@ -990,7 +984,7 @@ class Solver
// todo: cleandeps
foreach ($job['packages'] as $package) {
$rule = $this->createRemoveRule($package, self::RULE_JOB_REMOVE);
$rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE);
$this->addRule(RuleSet::TYPE_JOB, $rule);
$this->ruleToJob[$rule->getId()] = $job;
}
@ -998,9 +992,9 @@ class Solver
case 'lock':
foreach ($job['packages'] as $package) {
if (isset($this->installedMap[$package->getId()])) {
$rule = $this->createInstallRule($package, self::RULE_JOB_LOCK);
$rule = $this->createInstallRule($package, Rule::RULE_JOB_LOCK);
} else {
$rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK);
$rule = $this->createRemoveRule($package, Rule::RULE_JOB_LOCK);
}
$this->addRule(RuleSet::TYPE_JOB, $rule);
$this->ruleToJob[$rule->getId()] = $job;
@ -1028,7 +1022,7 @@ class Solver
//solver_prepare_solutions(solv);
if ($this->problems) {
throw new SolverProblemsException($this->problems, $this->learnedPool);
throw new SolverProblemsException($this->problems);
}
return $this->createTransaction();
@ -1487,22 +1481,21 @@ class Solver
$why = count($this->learnedPool) - 1;
assert($learnedLiterals[0] !== null);
$newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why);
$newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
return array($ruleLevel, $newRule, $why);
}
private function analyzeUnsolvableRule($conflictRule, &$lastWeakWhy)
private function analyzeUnsolvableRule($problem, $conflictRule, &$lastWeakWhy)
{
$why = $conflictRule->getId();
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
$learnedWhy = $this->learnedWhy[$why];
$problem = $this->learnedPool[$learnedWhy];
$problemRules = $this->learnedPool[$learnedWhy];
foreach ($problem as $problemRule) {
$this->analyzeUnsolvableRule($problemRule, $lastWeakWhy);
foreach ($problemRules as $problemRule) {
$this->analyzeUnsolvableRule($problem, $problemRule, $lastWeakWhy);
}
return;
}
@ -1520,24 +1513,22 @@ class Solver
}
if ($conflictRule->getType() == RuleSet::TYPE_JOB) {
$why = $this->ruleToJob[$conflictRule->getId()];
}
// if this problem was already found skip it
if (in_array($why, $this->problems[count($this->problems) - 1], true)) {
return;
$job = $this->ruleToJob[$conflictRule->getId()];
$problem->addJobRule($job, $conflictRule);
} else {
$problem->addRule($conflictRule);
}
$this->problems[count($this->problems) - 1][] = $why;
}
private function analyzeUnsolvable($conflictRule, $disableRules)
{
$lastWeakWhy = null;
$this->problems[] = array();
$this->learnedPool[] = array($conflictRule);
$problem = new Problem;
$problem->addRule($conflictRule);
$this->analyzeUnsolvableRule($problem, $conflictRule, $lastWeakWhy);
$this->analyzeUnsolvableRule($conflictRule, $lastWeakWhy);
$this->problems[] = $problem;
$seen = array();
$literals = $conflictRule->getLiterals();
@ -1569,9 +1560,9 @@ class Solver
}
$why = $this->decisionQueueWhy[$decisionId];
$this->learnedPool[count($this->learnedPool) - 1][] = $why;
$problem->addRule($why);
$this->analyzeUnsolvableRule($why, $lastWeakWhy);
$this->analyzeUnsolvableRule($problem, $why, $lastWeakWhy);
$literals = $why->getLiterals();
/* unnecessary because unlike rule.d, watch2 == 2nd literal, unless watch2 changed
@ -1591,7 +1582,6 @@ class Solver
if ($lastWeakWhy) {
array_pop($this->problems);
array_pop($this->learnedPool);
if ($lastWeakWhy->getType() === RuleSet::TYPE_JOB) {
$why = $this->ruleToJob[$lastWeakWhy];
@ -1616,8 +1606,12 @@ class Solver
}
if ($disableRules) {
foreach ($this->problems[count($this->problems) - 1] as $why) {
$this->disableProblem($why);
foreach ($this->problems[count($this->problems) - 1] as $reason) {
if ($reason['job']) {
$this->disableProblem($reason['job']);
} else {
$this->disableProblem($reason['rule']);
}
}
$this->resetSolver();
@ -1670,10 +1664,10 @@ class Solver
{
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
$why = $this->learnedWhy[$rule->getId()];
$problem = $this->learnedPool[$why];
$problemRules = $this->learnedPool[$why];
$foundDisabled = false;
foreach ($problem as $problemRule) {
foreach ($problemRules as $problemRule) {
if ($problemRule->disabled()) {
$foundDisabled = true;
break;

@ -19,47 +19,26 @@ class SolverProblemsException extends \RuntimeException
{
protected $problems;
public function __construct(array $problems, array $learnedPool)
public function __construct(array $problems)
{
$message = '';
foreach ($problems as $i => $problem) {
$message .= '[';
foreach ($problem as $why) {
$this->problems = $problems;
if (is_int($why) && isset($learnedPool[$why])) {
$rules = $learnedPool[$why];
} else {
$rules = $why;
}
parent::__construct($this->createMessage());
}
protected function createMessage()
{
$messages = array();
if (isset($rules['packages'])) {
$message .= $this->jobToText($rules);
} else {
$message .= '(';
foreach ($rules as $rule) {
if ($rule instanceof Rule) {
if ($rule->getType() == RuleSet::TYPE_LEARNED) {
$message .= 'learned: ';
}
$message .= $rule . ', ';
} else {
$message .= 'String(' . $rule . '), ';
}
}
$message .= ')';
}
$message .= ', ';
}
$message .= "]\n";
foreach ($this->problems as $problem) {
$messages[] = (string) $problem;
}
parent::__construct($message);
return "\n\tProblems:\n\t\t- ".implode("\n\t\t- ", $messages);
}
public function jobToText($job)
public function getProblems()
{
//$output = serialize($job);
$output = 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.implode(', ', $job['packages']).'])';
return $output;
return $this->problems;
}
}

@ -74,7 +74,6 @@ class FileDownloader implements DownloaderInterface
$url = $this->processUrl($url);
$this->rfs->copy($package->getSourceUrl(), $url, $fileName);
$this->io->write('');
if (!file_exists($fileName)) {
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'

@ -16,6 +16,7 @@ use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\Repository\RepositoryManager;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
/**
* Creates an configured instance of composer.
@ -38,7 +39,7 @@ class Factory
$composerFile = getenv('COMPOSER') ?: 'composer.json';
}
$file = new JsonFile($composerFile);
$file = new JsonFile($composerFile, new RemoteFilesystem($io));
if (!$file->exists()) {
if ($composerFile === 'composer.json') {
$message = 'Composer could not find a composer.json file in '.getcwd();
@ -98,7 +99,7 @@ class Factory
// init locker
$lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
$locker = new Package\Locker(new JsonFile($lockFile), $rm, md5_file($composerFile));
$locker = new Package\Locker(new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, md5_file($composerFile));
// initialize composer
$composer = new Composer();

@ -18,6 +18,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Request;
use Composer\DependencyResolver\Solver;
use Composer\DependencyResolver\SolverProblemsException;
use Composer\Downloader\DownloadManager;
use Composer\Installer\InstallationManager;
use Composer\IO\IOInterface;
@ -163,7 +164,7 @@ class Installer
$installFromLock = false;
$request = new Request($pool);
if ($this->update) {
$this->io->write('<info>Updating dependencies</info>');
$this->io->write('Updating dependencies');
$request->updateAll();
@ -174,7 +175,7 @@ class Installer
}
} elseif ($this->locker->isLocked()) {
$installFromLock = true;
$this->io->write('<info>Installing from lock file</info>');
$this->io->write('Installing from lock file');
if (!$this->locker->isFresh()) {
$this->io->write('<warning>Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies</warning>');
@ -192,7 +193,7 @@ class Installer
$request->install($package->getName(), $constraint);
}
} else {
$this->io->write('<info>Installing dependencies</info>');
$this->io->write('Installing dependencies');
$links = $this->collectLinks();
@ -206,7 +207,14 @@ class Installer
$solver = new Solver($policy, $pool, $installedRepo);
// solve dependencies
$operations = $solver->solve($request);
try {
$operations = $solver->solve($request);
} catch (SolverProblemsException $e) {
$this->io->write('<error>Your requirements could not be solved to an installable set of packages.</error>');
$this->io->write($e->getMessage());
return false;
}
// force dev packages to be updated to latest reference on update
if ($this->update) {
@ -299,6 +307,8 @@ class Installer
$eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
$this->eventDispatcher->dispatchCommandEvent($eventName);
}
return true;
}
private function collectLinks()

@ -17,6 +17,7 @@ use Composer\Composer;
use JsonSchema\Validator;
use Seld\JsonLint\JsonParser;
use Composer\Util\StreamContextFactory;
use Composer\Util\RemoteFilesystem;
/**
* Reads/writes json files.
@ -34,15 +35,22 @@ class JsonFile
const JSON_UNESCAPED_UNICODE = 256;
private $path;
private $rfs;
/**
* Initializes json file reader/parser.
*
* @param string $lockFile path to a lockfile
* @param RemoteFilesystem $rfs required for loading http/https json files
*/
public function __construct($path)
public function __construct($path, RemoteFilesystem $rfs = null)
{
$this->path = $path;
if (null === $rfs && preg_match('{^https?://}i', $path)) {
throw new \InvalidArgumentException('http urls require a RemoteFilesystem instance to be passed');
}
$this->rfs = $rfs;
}
public function getPath()
@ -67,15 +75,14 @@ class JsonFile
*/
public function read()
{
$ctx = StreamContextFactory::getContext(array(
'http' => array(
'header' => 'User-Agent: Composer/'.Composer::VERSION."\r\n"
)
));
$json = file_get_contents($this->path, false, $ctx);
if (!$json) {
throw new \RuntimeException('Could not read '.$this->path.', you are probably offline');
try {
if ($this->rfs) {
$json = $this->rfs->getContents($this->path, $this->path, false);
} else {
$json = file_get_contents($this->path);
}
} catch (\Exception $e) {
throw new \RuntimeException('Could not read '.$this->path.', you are probably offline ('.$e->getMessage().')');
}
return static::parseJson($json);

@ -15,6 +15,8 @@ namespace Composer\Repository;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\Util\RemoteFilesystem;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
@ -22,9 +24,10 @@ use Composer\Json\JsonFile;
class ComposerRepository extends ArrayRepository
{
protected $url;
protected $io;
protected $packages;
public function __construct(array $config)
public function __construct(array $config, IOInterface $io)
{
if (!preg_match('{^\w+://}', $config['url'])) {
// assume http as the default protocol
@ -36,12 +39,13 @@ class ComposerRepository extends ArrayRepository
}
$this->url = $config['url'];
$this->io = $io;
}
protected function initialize()
{
parent::initialize();
$json = new JsonFile($this->url.'/packages.json');
$json = new JsonFile($this->url.'/packages.json', new RemoteFilesystem($this->io));
$packages = $json->read();
if (!$packages) {
throw new \UnexpectedValueException('Could not parse package list from the '.$this->url.' repository');

@ -12,8 +12,10 @@
namespace Composer\Repository;
use Composer\IO\IOInterface;
use Composer\Package\Loader\ArrayLoader;
use Composer\Util\StreamContextFactory;
use Composer\Util\RemoteFilesystem;
use Composer\Downloader\TransportException;
/**
* @author Benjamin Eberlei <kontakt@beberlei.de>
@ -23,9 +25,10 @@ class PearRepository extends ArrayRepository
{
private $url;
private $channel;
private $streamContext;
private $io;
private $rfs;
public function __construct(array $config)
public function __construct(array $config, IOInterface $io, RemoteFilesystem $rfs = null)
{
if (!preg_match('{^https?://}', $config['url'])) {
$config['url'] = 'http://'.$config['url'];
@ -36,20 +39,17 @@ class PearRepository extends ArrayRepository
}
$this->url = rtrim($config['url'], '/');
$this->channel = !empty($config['channel']) ? $config['channel'] : null;
$this->io = $io;
$this->rfs = $rfs ?: new RemoteFilesystem($this->io);
}
protected function initialize()
{
parent::initialize();
set_error_handler(function($severity, $message, $file, $line) {
throw new \ErrorException($message, $severity, $severity, $file, $line);
});
$this->streamContext = StreamContextFactory::getContext();
$this->io->write('Initializing PEAR repository '.$this->url);
$this->fetchFromServer();
restore_error_handler();
}
protected function fetchFromServer()
@ -68,7 +68,7 @@ class PearRepository extends ArrayRepository
try {
$packagesLink = str_replace("info.xml", "packagesinfo.xml", $link);
$this->fetchPear2Packages($this->url . $packagesLink);
} catch (\ErrorException $e) {
} catch (TransportException $e) {
if (false === strpos($e->getMessage(), '404')) {
throw $e;
}
@ -81,7 +81,7 @@ class PearRepository extends ArrayRepository
/**
* @param string $categoryLink
* @throws ErrorException
* @throws TransportException
* @throws InvalidArgumentException
*/
private function fetchPearPackages($categoryLink)
@ -99,7 +99,7 @@ class PearRepository extends ArrayRepository
try {
$releasesXML = $this->requestXml($allReleasesLink);
} catch (\ErrorException $e) {
} catch (TransportException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
}
@ -120,8 +120,8 @@ class PearRepository extends ArrayRepository
);
try {
$deps = file_get_contents($releaseLink . "/deps.".$pearVersion.".txt", false, $this->streamContext);
} catch (\ErrorException $e) {
$deps = $this->rfs->getContents($this->url, $releaseLink . "/deps.".$pearVersion.".txt", false);
} catch (TransportException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
}
@ -226,6 +226,7 @@ class PearRepository extends ArrayRepository
{
$loader = new ArrayLoader();
$packagesXml = $this->requestXml($packagesLink);
$informations = $packagesXml->getElementsByTagName('pi');
foreach ($informations as $information) {
$package = $information->getElementsByTagName('p')->item(0);
@ -289,7 +290,7 @@ class PearRepository extends ArrayRepository
*/
private function requestXml($url)
{
$content = file_get_contents($url, false, $this->streamContext);
$content = $this->rfs->getContents($this->url, $url, false);
if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at '.$url.' did not respond.');
}

@ -35,11 +35,10 @@ class GitDriver extends VcsDriver
$this->repoDir = $this->url;
} else {
$this->repoDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/';
$repoDir = escapeshellarg($this->repoDir);
if (is_dir($this->repoDir)) {
$this->process->execute(sprintf('cd %s && git fetch origin', $repoDir), $output);
$this->process->execute('git fetch origin', $output, $this->repoDir);
} else {
$this->process->execute(sprintf('git clone %s %s', $url, $repoDir), $output);
$this->process->execute(sprintf('git clone %s %s', $url, escapeshellarg($this->repoDir)), $output);
}
}
@ -57,7 +56,7 @@ class GitDriver extends VcsDriver
if ($this->isLocal) {
// select currently checked out branch if master is not available
$this->process->execute(sprintf('cd %s && git branch --no-color', escapeshellarg($this->repoDir)), $output);
$this->process->execute('git branch --no-color', $output, $this->repoDir);
$branches = $this->process->splitLines($output);
if (!in_array('* master', $branches)) {
foreach ($branches as $branch) {
@ -69,7 +68,7 @@ class GitDriver extends VcsDriver
}
} else {
// try to find a non-master remote HEAD branch
$this->process->execute(sprintf('cd %s && git branch --no-color -r', escapeshellarg($this->repoDir)), $output);
$this->process->execute('git branch --no-color -r', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) {
if ($branch && preg_match('{/HEAD +-> +[^/]+/(\S+)}', $branch, $match)) {
$this->rootIdentifier = $match[1];
@ -114,7 +113,7 @@ class GitDriver extends VcsDriver
public function getComposerInformation($identifier)
{
if (!isset($this->infoCache[$identifier])) {
$this->process->execute(sprintf('cd %s && git show %s:composer.json', escapeshellarg($this->repoDir), escapeshellarg($identifier)), $composer);
$this->process->execute(sprintf('git show %s:composer.json', escapeshellarg($identifier)), $composer, $this->repoDir);
if (!trim($composer)) {
return;
@ -123,7 +122,7 @@ class GitDriver extends VcsDriver
$composer = JsonFile::parseJson($composer);
if (!isset($composer['time'])) {
$this->process->execute(sprintf('cd %s && git log -1 --format=%%at %s', escapeshellarg($this->repoDir), escapeshellarg($identifier)), $output);
$this->process->execute(sprintf('git log -1 --format=%%at %s', escapeshellarg($identifier)), $output, $this->repoDir);
$date = new \DateTime('@'.trim($output));
$composer['time'] = $date->format('Y-m-d H:i:s');
}
@ -139,7 +138,7 @@ class GitDriver extends VcsDriver
public function getTags()
{
if (null === $this->tags) {
$this->process->execute(sprintf('cd %s && git tag', escapeshellarg($this->repoDir)), $output);
$this->process->execute('git tag', $output, $this->repoDir);
$output = $this->process->splitLines($output);
$this->tags = $output ? array_combine($output, $output) : array();
}
@ -156,10 +155,9 @@ class GitDriver extends VcsDriver
$branches = array();
$this->process->execute(sprintf(
'cd %s && git branch --no-color --no-abbrev -v %s',
escapeshellarg($this->repoDir),
'git branch --no-color --no-abbrev -v %s',
$this->isLocal ? '' : '-r'
), $output);
), $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) {
if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
preg_match('{^(?:\* )? *(?:[^/]+/)?(\S+) *([a-f0-9]+) .*$}', $branch, $match);
@ -186,7 +184,7 @@ class GitDriver extends VcsDriver
if (static::isLocalUrl($url)) {
$process = new ProcessExecutor();
// check whether there is a git repo in that path
if ($process->execute(sprintf('cd %s && git tag', escapeshellarg($url)), $output) === 0) {
if ($process->execute('git tag', $output, $url) === 0) {
return true;
}
}

@ -35,7 +35,7 @@ class VcsRepository extends ArrayRepository
$this->url = $config['url'];
$this->io = $io;
$this->type = $config['type'];
$this->type = isset($config['type']) ? $config['type'] : 'vcs';
}
public function setDebug($debug)
@ -118,7 +118,7 @@ class VcsRepository extends ArrayRepository
}
} catch (\Exception $e) {
if ($debug) {
$this->io->write('Skipped tag '.$tag.', '.$e->getMessage());
$this->io->write('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()));
}
continue;
}

@ -0,0 +1,52 @@
<?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\Util;
/**
* Convert PHP errors into exceptions
*
* @author Artem Lopata <biozshock@gmail.com>
*/
class ErrorHandler
{
/**
* Error handler
*
* @param int $level Level of the error raised
* @param string $message Error message
* @param string $file Filename that the error was raised in
* @param int $line Line number the error was raised at
*
* @static
* @throws \ErrorException
*/
public static function handle($level, $message, $file, $line)
{
// respect error_reporting being disabled
if (!error_reporting()) {
return;
}
throw new \ErrorException($message, 0, $level, $file, $line);
}
/**
* Register error handler
*
* @static
*/
public static function register()
{
set_error_handler(array(__CLASS__, 'handle'));
}
}

@ -26,15 +26,16 @@ class ProcessExecutor
/**
* runs a process on the commandline
*
* @param $command the command to execute
* @param null $output the output will be written into this var if passed
* @param string $command the command to execute
* @param null $output the output will be written into this var if passed
* @param string $cwd the working directory
* @return int statuscode
*/
public function execute($command, &$output = null)
public function execute($command, &$output = null, $cwd = null)
{
$captureOutput = count(func_get_args()) > 1;
$this->errorOutput = null;
$process = new Process($command, null, null, null, static::getTimeout());
$process = new Process($command, $cwd, null, null, static::getTimeout());
$process->run(function($type, $buffer) use ($captureOutput) {
if ($captureOutput) {
return;

@ -12,6 +12,7 @@
namespace Composer\Util;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Downloader\TransportException;
@ -101,26 +102,50 @@ class RemoteFilesystem
}
$result = @file_get_contents($fileUrl, false, $ctx);
if (null !== $fileName) {
$result = @file_put_contents($fileName, $result) ? true : false;
}
// fix for 5.4.0 https://bugs.php.net/bug.php?id=61336
if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ 404}i', $http_response_header[0])) {
$result = false;
}
// decode gzip
if (false !== $result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
$decode = false;
foreach ($http_response_header as $header) {
if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
$decode = true;
continue;
} elseif (preg_match('{^HTTP/}i', $header)) {
$decode = false;
}
}
if ($decode) {
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
$result = zlib_decode($result);
} else {
// work around issue with gzuncompress & co that do not work with all gzip checksums
$result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
}
}
}
// handle copy command if download was successful
if (false !== $result && null !== $fileName) {
$result = (Boolean) @file_put_contents($fileName, $result);
}
// avoid overriding if content was loaded by a sub-call to get()
if (null === $this->result) {
$this->result = $result;
}
if ($this->progress) {
$this->io->overwrite(" Downloading", false);
$this->io->overwrite(" Downloading: <comment>100%</comment>");
}
if (false === $this->result) {
throw new TransportException("The '$fileUrl' file could not be downloaded");
throw new TransportException('The "'.$fileUrl.'" file could not be downloaded');
}
}
@ -138,7 +163,7 @@ class RemoteFilesystem
{
switch ($notificationCode) {
case STREAM_NOTIFY_FAILURE:
throw new TransportException(trim($message), $messageCode);
throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', $messageCode);
break;
case STREAM_NOTIFY_AUTH_REQUIRED:
@ -184,17 +209,21 @@ class RemoteFilesystem
}
}
protected function getOptionsForUrl($url)
protected function getOptionsForUrl($originUrl)
{
$options = array();
if ($this->io->hasAuthorization($url)) {
$auth = $this->io->getAuthorization($url);
$options['http']['header'] = 'User-Agent: Composer/'.Composer::VERSION."\r\n";
if (extension_loaded('zlib')) {
$options['http']['header'] .= 'Accept-Encoding: gzip'."\r\n";
}
if ($this->io->hasAuthorization($originUrl)) {
$auth = $this->io->getAuthorization($originUrl);
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
$options['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$options['http']['header'] .= "Authorization: Basic $authStr\r\n";
} elseif (null !== $this->io->getLastUsername()) {
$authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
$options['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$this->io->setAuthorization($url, $this->io->getLastUsername(), $this->io->getLastPassword());
$options['http']['header'] .= "Authorization: Basic $authStr\r\n";
$this->io->setAuthorization($originUrl, $this->io->getLastUsername(), $this->io->getLastPassword());
}
return $options;

@ -34,15 +34,15 @@ final class StreamContextFactory
// Handle system proxy
if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) {
// Some systems seem to rely on a lowercased version instead...
$proxy = isset($_SERVER['HTTP_PROXY']) ? $_SERVER['HTTP_PROXY'] : $_SERVER['http_proxy'];
$proxy = isset($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY'];
// http(s):// is not supported in proxy
$proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxy);
if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) {
throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
}
$options['http'] = array(
'proxy' => $proxy,
'request_fulluri' => true,

@ -40,9 +40,9 @@ class RequestTest extends TestCase
$this->assertEquals(
array(
array('packages' => array($foo), 'cmd' => 'install', 'packageName' => 'foo'),
array('packages' => array($bar), 'cmd' => 'install', 'packageName' => 'bar'),
array('packages' => array($foobar), 'cmd' => 'remove', 'packageName' => 'foobar'),
array('packages' => array($foo), 'cmd' => 'install', 'packageName' => 'foo', 'constraint' => null),
array('packages' => array($bar), 'cmd' => 'install', 'packageName' => 'bar', 'constraint' => null),
array('packages' => array($foobar), 'cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null),
),
$request->getJobs());
}
@ -63,11 +63,11 @@ class RequestTest extends TestCase
$pool->addRepository($repo2);
$request = new Request($pool);
$request->install('foo', $this->getVersionConstraint('=', '1'));
$request->install('foo', $constraint = $this->getVersionConstraint('=', '1'));
$this->assertEquals(
array(
array('packages' => array($foo1, $foo2), 'cmd' => 'install', 'packageName' => 'foo'),
array('packages' => array($foo1, $foo2), 'cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint),
),
$request->getJobs()
);

@ -59,13 +59,15 @@ class SolverTest extends TestCase
$this->repo->addPackage($this->getPackage('A', '1.0'));
$this->reposComplete();
$this->request->install('B');
$this->request->install('B', $this->getVersionConstraint('=', '1'));
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not resolve in exception.');
$this->fail('Unsolvable conflict did not result in exception.');
} catch (SolverProblemsException $e) {
// TODO assert problem properties
$problems = $e->getProblems();
$this->assertEquals(1, count($problems));
$this->assertEquals('The requested package "b" with constraint == 1.0.0.0 could not be found.', (string) $problems[0]);
}
}
@ -589,8 +591,10 @@ class SolverTest extends TestCase
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not resolve in exception.');
$this->fail('Unsolvable conflict did not result in exception.');
} catch (SolverProblemsException $e) {
$problems = $e->getProblems();
$this->assertEquals(1, count($problems));
// TODO assert problem properties
}
}
@ -610,8 +614,10 @@ class SolverTest extends TestCase
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not resolve in exception.');
$this->fail('Unsolvable conflict did not result in exception.');
} catch (SolverProblemsException $e) {
$problems = $e->getProblems();
$this->assertEquals(1, count($problems));
// TODO assert problem properties
}
}

@ -0,0 +1,49 @@
<?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\Util;
use Composer\Util\ErrorHandler;
use Composer\Test\TestCase;
/**
* ErrorHandler test case
*
* @author Artem Lopata <biozshock@gmail.com>
*/
class ErrorHandlerTest extends TestCase
{
/**
* Test ErrorHandler handles notices
*/
public function testErrorHandlerCaptureNotice()
{
$this->setExpectedException('\ErrorException', 'Undefined index: baz');
ErrorHandler::register();
$array = array('foo' => 'bar');
$array['baz'];
}
/**
* Test ErrorHandler handles warnings
*/
public function testErrorHandlerCaptureWarning()
{
$this->setExpectedException('\ErrorException', 'array_merge(): Argument #2 is not an array');
ErrorHandler::register();
array_merge(array(), 'string');
}
}

@ -31,7 +31,8 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue(null))
;
$this->assertEquals(array(), $this->callGetOptionsForUrl($io, array('http://example.org')));
$res = $this->callGetOptionsForUrl($io, array('http://example.org'));
$this->assertTrue(isset($res['http']['header']) && false !== strpos($res['http']['header'], 'User-Agent'), 'getOptions must return an array with a header containing a User-Agent');
}
public function testGetOptionsForUrlWithAuthorization()

@ -57,13 +57,13 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase
public function testHttpProxy()
{
$_SERVER['HTTP_PROXY'] = 'http://username:password@proxyserver.net:port/';
$_SERVER['http_proxy'] = 'http://proxyserver/';
$_SERVER['http_proxy'] = 'http://username:password@proxyserver.net:port/';
$_SERVER['HTTP_PROXY'] = 'http://proxyserver/';
$context = StreamContextFactory::getContext(array('http' => array('method' => 'GET')));
$options = stream_context_get_options($context);
$this->assertSame('http://proxyserver/', $_SERVER['http_proxy']);
$this->assertSame('http://proxyserver/', $_SERVER['HTTP_PROXY']);
$this->assertEquals(array('http' => array(
'proxy' => 'tcp://username:password@proxyserver.net:port/',

Loading…
Cancel
Save