Merge remote-tracking branch 'upstream/master' into svn-auth-reloaded
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)
|
||||
|
|
|
@ -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>`.
|
||||
|
||||
← [Libraries](02-libraries.md) | [Schema](04-schema.md) →
|
|
@ -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()) {
|
||||
|
||||
if ($rule->getType() == RuleSet::TYPE_JOB) {
|
||||
$why = $this->ruleToJob[$rule->getId()];
|
||||
} else {
|
||||
$why = $rule;
|
||||
}
|
||||
$this->problems[] = array($why);
|
||||
$problem = new Problem;
|
||||
|
||||
$this->disableProblem($why);
|
||||
if ($rule->getType() == RuleSet::TYPE_JOB) {
|
||||
$job = $this->ruleToJob[$rule->getId()];
|
||||
|
||||
$problem->addJobRule($job, $rule);
|
||||
$problem->addRule($conflict);
|
||||
$this->disableProblem($job);
|
||||
} else {
|
||||
$problem->addRule($rule);
|
||||
$problem->addRule($conflict);
|
||||
$this->disableProblem($rule);
|
||||
}
|
||||
$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()];
|
||||
} else {
|
||||
$why = $assertRule;
|
||||
}
|
||||
$this->problems[count($this->problems) - 1][] = $why;
|
||||
$job = $this->ruleToJob[$assertRule->getId()];
|
||||
|
||||
$this->disableProblem($why);
|
||||
$problem->addJobRule($job, $assertRule);
|
||||
$this->disableProblem($job);
|
||||
} else {
|
||||
$problem->addRule($assertRule);
|
||||
$this->disableProblem($assertRule);
|
||||
}
|
||||
}
|
||||
$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()];
|
||||
$job = $this->ruleToJob[$conflictRule->getId()];
|
||||
$problem->addJobRule($job, $conflictRule);
|
||||
} else {
|
||||
$problem->addRule($conflictRule);
|
||||
}
|
||||
|
||||
// if this problem was already found skip it
|
||||
if (in_array($why, $this->problems[count($this->problems) - 1], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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($conflictRule, $lastWeakWhy);
|
||||
$this->analyzeUnsolvableRule($problem, $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());
|
||||
}
|
||||
|
||||
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";
|
||||
protected function createMessage()
|
||||
{
|
||||
$messages = array();
|
||||
|
||||
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…
Reference in New Issue