diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php
index d6fe2844f..49cb138d8 100644
--- a/src/Composer/Command/ConfigCommand.php
+++ b/src/Composer/Command/ConfigCommand.php
@@ -456,6 +456,10 @@ EOT
);
if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) {
+ if ($settingKey === 'disable-tls' && $this->config->get('disable-tls')) {
+ $this->getIO()->writeError('You are now running Composer with SSL/TLS protection enabled.');
+ }
+
return $this->configSource->removeConfigSetting($settingKey);
}
if (isset($uniqueConfigValues[$settingKey])) {
@@ -640,7 +644,17 @@ EOT
));
}
- return call_user_func(array($this->configSource, $method), $key, $normalizer($values[0]));
+ $normalizedValue = $normalizer($values[0]);
+
+ if ($key === 'disable-tls') {
+ if (!$normalizedValue && $this->config->get('disable-tls')) {
+ $this->getIO()->writeError('You are now running Composer with SSL/TLS protection enabled.');
+ } elseif ($normalizedValue && !$this->config->get('disable-tls')) {
+ $this->getIO()->writeError('You are now running Composer with SSL/TLS protection disabled.');
+ }
+ }
+
+ return call_user_func(array($this->configSource, $method), $key, $normalizedValue);
}
protected function handleMultiValue($key, array $callbacks, array $values, $method)
diff --git a/src/Composer/DependencyResolver/Decisions.php b/src/Composer/DependencyResolver/Decisions.php
index a9808e60e..86b62c3d3 100644
--- a/src/Composer/DependencyResolver/Decisions.php
+++ b/src/Composer/DependencyResolver/Decisions.php
@@ -196,4 +196,16 @@ class Decisions implements \Iterator, \Countable
$this->decisionMap[$packageId] = -$level;
}
}
+
+ public function __toString()
+ {
+ $decisionMap = $this->decisionMap;
+ ksort($decisionMap);
+ $str = '[';
+ foreach ($decisionMap as $packageId => $level) {
+ $str .= $packageId.':'.$level.',';
+ }
+ $str .= ']';
+ return $str;
+ }
}
diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php
index c3128c6c4..66a43c37b 100644
--- a/src/Composer/DependencyResolver/Solver.php
+++ b/src/Composer/DependencyResolver/Solver.php
@@ -478,7 +478,7 @@ class Solver
unset($seen[abs($literal)]);
if ($num && 0 === --$num) {
- $learnedLiterals[0] = -abs($literal);
+ $learnedLiterals[0] = -$literal;
if (!$l1num) {
break 2;
diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php
index 9508f5886..261345930 100644
--- a/src/Composer/Repository/RepositoryFactory.php
+++ b/src/Composer/Repository/RepositoryFactory.php
@@ -94,6 +94,9 @@ class RepositoryFactory
if (!$config) {
$config = Factory::createConfig($io);
}
+ if ($io) {
+ $io->loadConfiguration($config);
+ }
if (!$rm) {
if (!$io) {
throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager');
diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php
index 7ad4f8f65..ccb1810de 100644
--- a/tests/Composer/Test/DependencyResolver/SolverTest.php
+++ b/tests/Composer/Test/DependencyResolver/SolverTest.php
@@ -846,6 +846,70 @@ class SolverTest extends TestCase
));
}
+ /**
+ * Tests for a bug introduced in commit 451bab1c2cd58e05af6e21639b829408ad023463 Solver.php line 554/523
+ *
+ * Every package and link in this test matters, only a combination this complex will run into the situation in which
+ * a negatively decided literal will need to be learned inverted as a positive assertion.
+ *
+ * In particular in this case the goal is to first have the solver decide X 2.0 should not be installed to later
+ * decide to learn that X 2.0 must be installed and revert decisions to retry solving with this new assumption.
+ */
+ public function testLearnPositiveLiteral()
+ {
+ $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
+ $this->repo->addPackage($packageC1 = $this->getPackage('C', '1.0'));
+ $this->repo->addPackage($packageC2 = $this->getPackage('C', '2.0'));
+ $this->repo->addPackage($packageD = $this->getPackage('D', '1.0'));
+ $this->repo->addPackage($packageE = $this->getPackage('E', '1.0'));
+ $this->repo->addPackage($packageF1 = $this->getPackage('F', '1.0'));
+ $this->repo->addPackage($packageF2 = $this->getPackage('F', '2.0'));
+ $this->repo->addPackage($packageG1 = $this->getPackage('G', '1.0'));
+ $this->repo->addPackage($packageG2 = $this->getPackage('G', '2.0'));
+ $this->repo->addPackage($packageG3 = $this->getPackage('G', '3.0'));
+
+ $packageA->setRequires(array(
+ 'b' => new Link('A', 'B', $this->getVersionConstraint('==', '1.0'), 'requires'),
+ 'c' => new Link('A', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'),
+ 'd' => new Link('A', 'D', $this->getVersionConstraint('==', '1.0'), 'requires'),
+ ));
+
+ $packageB->setRequires(array(
+ 'e' => new Link('B', 'E', $this->getVersionConstraint('==', '1.0'), 'requires'),
+ ));
+
+ $packageC1->setRequires(array(
+ 'f' => new Link('C', 'F', $this->getVersionConstraint('==', '1.0'), 'requires'),
+ ));
+ $packageC2->setRequires(array(
+ 'f' => new Link('C', 'F', $this->getVersionConstraint('==', '1.0'), 'requires'),
+ 'g' => new Link('C', 'G', $this->getVersionConstraint('>=', '1.0'), 'requires'),
+ ));
+
+ $packageD->setRequires(array(
+ 'f' => new Link('D', 'F', $this->getVersionConstraint('>=', '1.0'), 'requires'),
+ ));
+
+ $packageE->setRequires(array(
+ 'g' => new Link('E', 'G', $this->getVersionConstraint('<=', '2.0'), 'requires'),
+ ));
+
+ $this->reposComplete();
+
+ $this->request->install('A');
+
+ $this->checkSolverResult(array(
+ array('job' => 'install', 'package' => $packageF1),
+ array('job' => 'install', 'package' => $packageD),
+ array('job' => 'install', 'package' => $packageG2),
+ array('job' => 'install', 'package' => $packageC2),
+ array('job' => 'install', 'package' => $packageE),
+ array('job' => 'install', 'package' => $packageB),
+ array('job' => 'install', 'package' => $packageA),
+ ));
+ }
+
protected function reposComplete()
{
$this->repoSet->addRepository($this->repoInstalled);
diff --git a/tests/Composer/Test/Fixtures/installer/update-requiring-decision-reverts-and-learning-positive-literals.test b/tests/Composer/Test/Fixtures/installer/update-requiring-decision-reverts-and-learning-positive-literals.test
new file mode 100644
index 000000000..3f5667823
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-requiring-decision-reverts-and-learning-positive-literals.test
@@ -0,0 +1,100 @@
+--TEST--
+Update a project which requires decision reverts and learning a positive literal to arrive at a correct solution.
+
+Tests for solver regression in commit 451bab1c2cd58e05af6e21639b829408ad023463. See also SolverTest testLearnPositiveLiteral
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "spryker-feature/product",
+ "require": {
+ "spryker-feature/spryker-core": "1.0.0",
+ "spryker-shop/product-search-widget": ">=1.0.0",
+ "spryker/product-category-filter-gui": "1.0.0"
+ },
+ "version": "1.0.0"
+ },
+ {
+ "name": "spryker-feature/spryker-core",
+ "version": "1.0.0",
+ "require": {
+ "spryker/store": "1.0.0"
+ }
+ },
+ {
+ "name": "spryker/store",
+ "version": "1.0.0",
+ "require": {
+ "spryker/kernel": "<=2.0.0"
+ }
+ },
+ {
+ "name": "spryker-shop/product-search-widget",
+ "version": "1.0.0",
+ "require": {
+ "spryker/catalog": "1.0.0"
+ }
+ },
+ {
+ "name": "spryker-shop/product-search-widget",
+ "version": "2.0.0",
+ "require": {
+ "spryker/catalog": "1.0.0",
+ "spryker/kernel": ">=1.0.0"
+ }
+ },
+ {
+ "name": "spryker/product-category-filter-gui",
+ "version": "1.0.0",
+ "require": {
+ "spryker/catalog": ">=1.0.0"
+ }
+ },
+ {
+ "name": "spryker/catalog",
+ "version": "1.0.0",
+ "require": { }
+ },
+ {
+ "name": "spryker/catalog",
+ "version": "2.0.0",
+ "require": { }
+ },
+
+ {
+ "name": "spryker/kernel",
+ "version": "1.0.0",
+ "require": { }
+ },
+ {
+ "name": "spryker/kernel",
+ "version": "2.0.0",
+ "require": {
+ }
+ },
+ {
+ "name": "spryker/kernel",
+ "version": "3.0.0",
+ "require": { }
+ }
+ ]
+ }
+ ],
+ "require": {
+ "spryker-feature/product": "1.0.0"
+ }
+}
+--RUN--
+update
+--EXPECT--
+Installing spryker/catalog (1.0.0)
+Installing spryker/product-category-filter-gui (1.0.0)
+Installing spryker/kernel (2.0.0)
+Installing spryker-shop/product-search-widget (2.0.0)
+Installing spryker/store (1.0.0)
+Installing spryker-feature/spryker-core (1.0.0)
+Installing spryker-feature/product (1.0.0)
+